簡體   English   中英

為什么需要多個shared_future對象來同步數據

[英]why are multiple shared_future objects needed to synchronize data

指向數據結構的指針通過std::promisestd::shared_future與多個線程共享。 從安東尼·威廉姆斯(第85-86頁)的《 C ++並發行動 》一書中,似乎只有當每個接收線程使用std::shared_future對象的副本而不是每個線程訪問單個對象時,才正確地同步數據。 ,全局std::shared_future

為了說明這一點,考慮一個創建大bigdata並將一個指針傳遞給具有只讀訪問權限的多個線程的線程。 如果未正確處理線程之間的數據同步,則內存重新排序可能導致未定義的行為(例如, worker_thread讀取不完整的數據)。

此(不正確的?)實現使用單個全局std::shared_future

#include <future>

struct bigdata { ... };

std::shared_future<bigdata *> global_sf;

void worker_thread()
{
    const bigdata *ptr = global_sf.get();
    ...  // ptr read-only access
}

int main()
{
    std::promise<bigdata *> pr;
    global_sf = pr.get_future().share();

    std::thread t1{worker_thread};
    std::thread t2{worker_thread};

    pr.set_value(new bigdata);
    ...
}

在這個(正確的)實現中,每個worker_thread都獲得一個std::shared_future worker_thread的副本:

void worker_thread(std::shared_future<bigdata *> sf)
{
    const bigdata *ptr = sf.get();
    ...
}

int main()
{
    std::promise<bigdata *> pr;
    auto sf = pr.get_future().share();

    std::thread t1{worker_thread, sf};
    std::thread t2{worker_thread, sf};

    pr.set_value(new bigdata);
    ....

我想知道為什么第一個版本不正確。

如果std::shared_future::get()是一個非const成員函數,那將是有道理的,因為從多個線程訪問單個std::shared_future本身就是一場數據競賽。 但是由於此成員函數被聲明為const,並且global_sf對象與線程同步,因此可以安全地從多個線程並行訪問。

我的問題是,為什么只有每個worker_thread接收到std::shared_future worker_thread的副本時,才能保證它能夠正常工作?

您的使用單個全局shared_future是完全可以的,即使有些不尋常,這本書也可能是錯誤的。

[futures.shared_future]¶2

[ 注意: shared_future成員函數不與自己同步,但與共享狀態同步。 尾注 ]

注釋是非規范性的,因此上述內容多余地使一個事實變得明確,該事實已在規范性措詞中隱含。

[intro.races]¶2

如果兩個表達式求值之一修改一個內存位置,而另一個表達式讀取或修改了相同的內存位置,則這兩個表達式求值會沖突

¶6

某些庫調用另一個線程執行的其他庫調用同步

[...在與...同步之前 ,其他段落的定義發生了。]

¶19

如果兩個動作是由不同的線程執行的,則這兩個動作可能是並發的...如果程序包含兩個潛在的並發沖突的動作,其中至少一個不是原子的,並且兩個動作都不在另一個之前,則程序的執行包含數據爭用 。 。

[res.on.data.races]¶3

C ++標准庫函數不得直接或間接修改可由當前線程以外的線程訪問的對象,除非通過該函數的非常量參數(包括this直接或間接訪問這些對象。

因此,我們知道在不同線程中對global_sf.get()調用可能是並發的,除非您為它們提供了額外的同步(例如互斥鎖)。 但我們也知道,在不同線程中對global_sf.get()調用不會沖突,因為它是const方法,因此禁止修改可從多個線程(包括*this訪問的對象。 因此,不滿足數據爭用的定義(未排序,可能同時發生的沖突操作),程序不包含數據爭用。

人們通常還是希望避免使用全局變量,但這是一個單獨的問題。

請注意,如果這本書是正確的,則說明存在矛盾。 它聲稱是正確的代碼仍然包含一個全局shared_future ,當它們創建本地副本時,可以從多個線程訪問它們:

void worker_thread()
{
    auto local_sf = global_sf; // <-- unsynchronized access of global_sf here

    const bigdata *ptr = local_sf.get();
    ...
}

暫無
暫無

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

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