2012-01-25 40 views
7

這是通用原子交換函數的正確實現嗎?我在GCC上尋找C++ 03兼容的解決方案。使用gcc原子內置函數的原子交換函數

template<typename T> 
void atomic_swap(T & a, T & b) { 
    static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); 
    T * ptr = &a; 
    b =__sync_lock_test_and_set(ptr, b); 
    __sync_lock_release(&ptr); 
} 

如果不是,我該怎麼辦才能修復它?

另外:是__sync_lock_release總是有必要的嗎?當通過其他代碼庫進行搜索時,我發現這通常不會被調用。如果不釋放調用我的代碼看起來是這樣的:

template<typename T> 
void atomic_swap(T & a, T & b) { 
    static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); 
    b = __sync_lock_test_and_set(&a, b); 
} 

PS:Atomic swap in GNU C++是一個類似的問題,但因爲提供的答案需要C++ 11的std::atomic,它有簽名Data *swap_data(Data *new_data)它沒有按」它沒有回答我的問題t對於swap函數似乎是有意義的。 (它實際上是使用在函數前定義的全局變量交換提供的參數。)

+0

它看起來像只訪問'a'應該是原子? –

+0

爲什麼重新發明輪子?看看http://concurrencykit.org/ – 2012-01-27 22:08:57

+0

@BenVoigt那篇文章沒有給出明確的答案。它用一個全局變量交換參數。 – StackedCrooked

回答

9

請記住,此版本的交換不是完全原子操作。雖然b的值將被原子複製到a,但a的值可能會被另一個線程複製到另一個修改值b。換句話說,對b的賦值對於其他線程來說不是原子的。因此,您可能會遇到以下情況:a == 1b == 2以及內置的gcc後,最終返回a == 2並返回1的值,但現在另一個線程已將b的值更改爲3,而您在b中寫入該值,其值爲1。因此,儘管您可能已經「技術上」交換了這些值,但是您沒有自動執行該操作......另一個線程觸及了b的值,該值位於gcc原子內置的返回值與該返回值的賦值之間b。在從裝配臺點來看,你有類似如下:

lea RAX, qword ptr [RDI] // T * ptr = &a; 
mov RCX, qword ptr [RSI] // copy out the value referenced by b into a register 
xchg [RAX], RCX   // __sync_lock_test_and_set(&a, b) 
mov qword ptr [RSI], RCX // place the exchange value back into b (not atomic!!) 

說實話,你不能這樣做的兩個獨立的內存位置的無鎖的原子交換,而不象DCAS硬件操作或一個弱負載鏈接/存儲條件,或者可能使用其他方法,如事務性內存(本身往往使用細粒度鎖定)。其次,由於你的函數是現在編寫的,如果你想讓你的原子操作同時獲得和釋放語義,那麼是的,你將不得不放置在__sync_lock_release中,否則你將要必須通過__sync_synchronize添加完整的內存屏障。否則它只會獲得__sync_lock_test_and_set上的語義。儘管如此,它不會自動交換兩個單獨的內存位置彼此...

+0

我知道寫入'b'使其成爲非原子操作,因此不是線程安全的。但是,我沒有看到一種安全的方法。這是否意味着無法實現指針類型的無鎖交換? – StackedCrooked

+0

非常多,除非再次,硬件支持類似於DCAS的東西,或者負載鏈接/存儲條件操作非常弱,以至於如果有任何對內存的訪問或者至少在高速緩存行上的內存訪問,而你交換的兩個值在同一個緩存行上 – Jason