2010-03-19 41 views
17

我想驗證我的理解是正確的。這種事情很棘手,所以我幾乎肯定我錯過了一些東西。我有一個由實時線程和非實時線程組成的程序。我希望非RT線程能夠將指針交換到由RT線程使用的內存。原子交換在GNU C++

從文檔,我的理解是,這可以在g++與實現:

// global 
Data *rt_data; 

Data *swap_data(Data *new_data) 
{ 
#ifdef __GNUC__ 
    // Atomic pointer swap. 
    Data *old_d = __sync_lock_test_and_set(&rt_data, new_data); 
#else 
    // Non-atomic, cross your fingers.           
    Data *old_d = rt_data; 
    rt_data = new_data; 
#endif 
    return old_d; 
} 

這在rt_data修改程序(比初始設置等)的唯一地方。當在實時上下文中使用rt_data時,它被複制到本地指針。對於old_d,稍後確定不使用舊內存時,它將在非RT線程中釋放。它是否正確?我需要volatile嗎?我應該調用其他同步原語嗎?

通過我在C++這樣的方式,但我感興趣的答案是否爲不同C.

提前

感謝。

回答

24

當在C/C++中寫入併發代碼時,一般不使用volatilevolatile的語義非常接近你想要的,它是誘人的,但最終易變的是not enough。不幸的是Java/C# volatile != C/C++ volatile。香草薩特有一個很好的解釋混亂的混亂article

你真正想要的是一個內存圍欄。 __sync_lock_test_and_set爲您提供擊劍。

將rt_data指針複製(加載)到本地副本時,您還需要一個內存圍欄。

鎖定免費的編程是棘手的。如果你願意使用海灣合作委員會的C++ 0x擴展,它會更容易:

#include <cstdatomic> 

std::atomic<Data*> rt_data; 

Data* swap_data(Data* new_data) 
{ 
    Data* old_data = rt_data.exchange(new_data); 
    assert(old_data != new_data); 
    return old_data; 
} 

void use_data() 
{ 
    Data* local = rt_data.load(); 
    /* ... */ 
} 
+0

謝謝!我可以按照你的建議使用std :: atomic,這非常好。 (我還不熟悉最新的C++ 0x)。出於好奇,如果我使用__sync_lock_test_and_set,讀取時使用的正確圍欄是什麼? (即,製作本地副本) – Steve 2010-03-20 15:23:51

3

更新:這個答案是不正確的,因爲我失蹤的事實,volatile保證訪問volatile變量不會重新排序,但不提供這種保證相對於其他非volatile訪問和操作。內存圍欄確實提供了這樣的保證,並且對於這個應用程序是必需的。我原來的答案在下面,但不要採取行動。在我的理解中,請參閱this answer以獲得一個很好的解釋,導致以下不正確的響應。

原來的答覆:

是的,你需要volatilert_data聲明; 任何時候一個變量可以在一個線程的訪問流程之外被修改,它應該被聲明爲volatile。由於您複製到本地指針,因此您可以在不使用volatile的情況下離開,但至少可以幫助您處理文檔,並禁止可能導致問題的某些編譯器優化。請看下面的例子,從DDJ通過:

volatile int a; 
int b; 
a = 1; 
b = a; 

如果有可能aa=1b=a之間的價值變化,那麼a應聲明volatile(除非,當然,分配的失日期值爲b是可以接受的)。多線程,特別是原子基元,構成了這種情況。這種情況也是由信號處理程序修改的變量和映射到奇數存儲單元(如硬件I/O寄存器)的變量觸發的。另見this question

否則,這對我來說很好。

在C中,我可能會使用GLib爲此提供的原子基元。如果原子操作不可用,他們將在可用的情況下使用原子操作並回退到基於緩慢但基於互斥體的實現。 Boost可能爲C++提供了類似的東西。

+5

揮發性與併發無關,它們是完全正交的。它們都處理強制加載/存儲和重新排序,但由volatile提供的保證不能解決併發問題。 – 2010-03-19 17:37:26

+0

@Caspin是的,易失性與併發性問題是正交的,僅僅是易失性是不夠的。不過,我的理解是volatile在併發編程中很有用,以確保線程看到對方的變化。一個變量被另一個線程改變並且被一個硬件中斷改變沒有什麼區別 - 兩者都違反了加載/存儲重新排序所需的假設,而volatile會告訴編譯器這些假設不一定成立。 – 2010-03-19 21:29:48

+0

正交意味着volatile解決方案的問題不是併發編程創建的問題。特別是,volatile的訂單保證僅適用於當前線程,並且僅適用於其他揮發性物質。請閱讀我答案中提供的鏈接,因爲volatile的整體描述過於複雜,不適合評論。或者更好的問題stackoverflow「爲什麼不是volatile對c/C++併發編程有用?」我會提供一個深入的答案。 – 2010-03-19 22:34:04