在閱讀一本有關 c 多執行緒編程的書時,我遇到了下面的一個示例。
void f(int i,std::string const& s);
void oops(int some_param)
{
char buffer[1024];
sprintf(buffer, "%i",some_param);
std::thread t(f,3,buffer);
t.detach();
}
在這種情況下,傳遞給新執行緒的是指向區域變數緩沖區的指標,并且函式 oops 很有可能在緩沖區被轉換為新執行緒上的 std::string 之前退出,從而導致到未定義的行為。
我知道它是 char* 型別的引數緩沖區,而不是 std::string 被復制到執行緒 t 的內部存盤中。讓我感到困惑的是,即使已經構造了執行緒 t,也有可能沒有發生從 char* 到 std::string 的轉換。那么什么時候會發生轉換呢?是在作業系統計劃執行執行緒之前嗎?
uj5u.com熱心網友回復:
正如您所說,陣列引數衰減為 a char*,然后為執行緒復制。更具體地說,新創建的執行緒基本上執行以下運算式:
std::invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...)
其中f和args是std::thread建構式的引數,并且auto(/*...*/)是新的 C 23 語法,它從引數創建引數衰減型別的純右值(并在系結到引數中的參考時從它實作一個臨時物件std::invoke) .
但是,在此評估中,臨時物件auto(/*...*/)的具體化是在執行緒建構式的呼叫者的背景關系中執行的,并且呼叫的開始與建構式std::invoke的完成同步std::thread。
這意味著auto(/*...*)衰減的引數副本是在執行緒開始執行之前制作的,但其他一切都是作為新執行執行緒的一部分發生的。
所以auto(buffer)將給出一個型別的臨時物件,char*該物件一直存在到執行緒函式呼叫結束(因為臨時物件一直存在到創建它們的完整運算式結束)。然后std::invoke以對該臨時物件的參考作為引數呼叫。此呼叫發生在新創建的執行執行緒中,并且此處的所有內容都與原始執行緒中的其余執行不同步。
std::invoke基本上f將給它的副本稱為第一個引數,并將其他引數轉發給f. 現在,由于您f希望將std::string const&第二個引數作為第二個引數,但std::invoke會將其作為引數傳遞,因此將物化char*一個臨時物件,從 , 初始化,以供系結到的參考。因此,這種構造發生在呼叫的背景關系中,該呼叫已經與在主執行緒中指標指向的陣列的潛在破壞不同步。std::stringchar*std::invoke
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/508313.html
