2017-04-03 36 views
1

我理解C++ 11中內存排序的基本規則,特別是release-acquire排序。我在兩個線程之間共享了一大塊內存,我不需要原子性,但是要確保線程完成的所有更改最終都可以在另一個線程中看到,尤其是在具有寬鬆內存模型的平臺上。不鎖定線程之間的數據可見性

可以簡單地使用原子防護變量var來觸發內存同步嗎?例如,

std::atomic<bool> guardVar; 
char *shared_mem=get_shared_mem(); 

(thread 1) 
while(true) { 
    happens_many_things(); 
    do_whatever_I_want_with_shared_mem(); 
    guardVar.store(0, std::memory_order_release); 
} 

(in thread 2) 
while(true) { 
    guardVar.load(std::memory_order_acquire); 
    read_shared_mem_no_problem_if_inconsistent(); 
} 

再次,這是不是一個問題,如果線程2 do_whatever_I_want_with_shared_mem(),我只是想確保我得到線程1所寫的所有變化的中間讀「半就緒」狀態在一個明確的點之後。

基於this article它應該工作,但我沒有在網絡上看到這樣的解決方案,並且不容易測試它是否真的按照我的意圖進行測試。

可以嗎?如果是這樣,有沒有更優雅的方式?

回答

1

它不是一個問題,如果線程2 do_whatever_I_want_with_shared_mem()

這是一個錯誤,你不能被多個線程訪問共享內存中讀取一個「半準備」狀態,如果一個他們正在修改數據。 C++標準將其稱爲數據競爭,並導致未定義的行爲。

兩個線程之間的訪問需要同步,但是您使用std::atomic的方式不正確。線程1中的store_release緊接着再次訪問相同的數據。 load_acquire也是如此;兩個操作之間沒有同步,因此您正在處理數據競爭。

爲了確保您的共享存儲器由一個線程一次只能訪問,guardVar技術上可以使用這樣的:

std::atomic<bool> guardVar{false}; 

(thread 1) 
while(true) { 

    while (guardVar.exchange(true, std::memory_order_acquire)); // LOCK 

    happens_many_things(); 
    do_whatever_I_want_with_shared_mem(); 

    guardVar.store(false, std::memory_order_release); // UNLOCK 
} 

(in thread 2) 
while(true) { 

    while (guardVar.exchange(true, std::memory_order_acquire)); // LOCK 

    read_shared_mem_no_problem_if_inconsistent(); 

    guardVar.store(false, std::memory_order_release); // UNLOCK 
} 

但由於這是使用std::atomic在一個相當低效的方式互斥(注意紡),你真的應該使用std::mutex

更新:

它仍然可以使用您的共享我mory沒有鎖定,但是你有責任確保在共享內存中訪問的每個單獨對象都是無數據競爭的(對象有資格獲得std::atomic)。

然後,您或多或少地獲得您在第二個線程可能看到「半準備」狀態(某些對象已更新,其他則不是)的問題中描述的行爲。如果沒有同步,第二個線程無法真正知道第一個線程的更新何時完成,但至少可以安全地同時讀取/寫入數據競爭自由對象。

+0

嗯。你是對的,這確實是一個未定義的行爲。 – Ferenc

+0

嗯。你說得對,這是標準確實是一個未定義的行爲。 想象一下,我模擬一個幀緩衝區,一個線程在隨機訪問中連續寫入的內容,另一個線程想定期讀出整個緩衝區來處理它。撕裂不是問題。 我怎樣才能解決這個沒有任何類型的鎖定在任何線程? – Ferenc

+0

@Ferenc這個場景確實是你問題的一部分..我會更新答案 – LWimsey