簡體   English   中英

std :: call_once和內存重新排序

[英]std::call_once and memory reordering

鑒於此處的代碼:

class lazy_init
{
    mutable std::once_flag flag;
    mutable std::unique_ptr<expensive_data> data;

    void do_init() const
    {
        data.reset(new expensive_data);
    }
public:
    expensive_data const& get_data() const
    {
        std::call_once(flag,&lazy_init::do_init,this);
        return *data;
    }
};

我在其他地方也看到了相同模式的一些變體。 所以我的問題是:為什么這段代碼被認為是保存? 為什么編譯器在調用std :: call_once之前不能只讀取數據並最終得到不正確的數據? 例如

tmp = data.get();
std::call_once(flag,&lazy_init::do_init,this);
return *tmp;

我的意思是我沒有找到任何可以阻止這種情況的障礙。

如果允許編譯器生成與您描述的匹配的代碼,那么用C ++編程基本上是不可能的。

這在§1.9/ 14 程序執行 (n3290)中說明:

在與要評估的下一個全表達式相關聯的每個值計算和副作用之前,對與全表達式相關聯的每個值計算和副作用進行排序。

您的return語句在前一個完整表達式之后排序。 編譯器必須輸出代碼,就好像在評估return語句之前已完全評估了前一個語句的所有副作用。
您的示例不遵守該規則,因為它在考慮std::call_once(...)完整表達式的副作用之前評估*data

另外, std::call_once在其描述中有這個(§30.4.4.2/ 2和3):

2 / 效果 :不調用其func的call_once的執行是被動執行。 調用其func的call_once的執行是一個活動執行。 活動執行應調用INVOKE (DECAY_- COPY ( std::forward<Callable>(func)), DECAY_COPY (std::forward<Args>(args))...) 如果對func的這種調用拋出異常,則執行異常,否則返回。 異常執行應將異常傳播給call_once的調用者。 在任何給定的once_flag的call_once的所有執行中:最多一個應該是返回執行; 如果有返回執行,則應該是最后一次執行; 只有在返回執行時才會執行被動執行。 [ 注意:被動執行允許其他線程可靠地觀察先前返回執行產生的結果。 - 結束說明 ]

3 / 同步 :對於任何給定的once_flag:所有活動執行都按總順序發生; 活動執行的完成與此總訂單中下一個的開始同步; 並且返回的執行與所有被動執行的返回同步。

因此標准要求同步以適合您的用例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM