該操作可能實際上不會將新值存儲到目標中,因爲與另一個線程的比賽會在您嘗試同時更改該值。 CAS原語不保證寫入發生 - 只有當該值已經達到預期值時纔會發生寫入。如果該值不符合預期,那麼該原語無法知道正確的行爲,因此在這種情況下沒有任何反應 - 您需要通過檢查返回值來查看操作是否正常工作,從而解決問題。
所以,你的例子:
elem->next = __sync_val_compare_and_swap(&head, head, elem); //always inserts?
不一定會插入新的元素。如果另一個線程在同一時刻插入一個元素,則存在爭用條件,可能會導致此線程調用__sync_val_compare_and_swap()
而不更新head
(但如果正確處理該線程或其他線程的元素,則不會丟失)。
但是,有一個與該行的代碼另一個問題 - 即使head
沒有得到更新,還有的時間,其中head
點到插入的元素,但該元素的next
指針尚未更新爲指向一個短暫的瞬間列表的前一個頭。如果另一個線程在那段時間內猛撲過來並試圖走出列表,就會發生不好的事情。
正確更新列表中更改了該行的代碼是這樣的:
whatever_t* prev_head = NULL;
do {
elem->next = head; // set up `elem->head` so the list will still be linked
// correctly the instant the element is inserted
prev_head = __sync_val_compare_and_swap(&head, elem->next, elem);
} while (prev_head != elem->next);
或者使用bool
變種,這我覺得是一個比較方便:
do {
elem->next = head; // set up `elem->head` so the list will still be linked
// correctly the instant the element is inserted
} while (!__sync_bool_compare_and_swap(&head, elem->next, elem));
這是一種醜陋的,我希望我的理解正確(在線程安全代碼的細節中很容易被絆住)。它應該包裝在insert_element()
函數中(或者甚至更好,使用適當的庫)。
解決ABA問題:
我不認爲ABA問題是有關這個「元素添加到列表頭」的代碼。假設一個線程想要將對象X
添加到列表中,並在執行elem->next = head
時,head
的值爲A1
。
然後執行__sync_val_compare_and_swap()
之前,另一組線程走來和:
- 從列表中刪除
A1
,使得head
點B
- 做什麼用的對象
A1
並釋放它
- 分配另一個對象,
A2
碰巧與A1
相同的地址是
- 增加了
A2
的列表,以便head
現在指向A2
由於A1
和A2
具有相同標識符/地址,這是ABA問題的一個實例。
但是,它並沒有在這種情況下,因爲線程加入對象X
不關心的head
指向不同的對象比它開始了關係 - 所有這關心的是,當X
排隊:
- 名單是一致的,
- 名單上沒有對象已經消失,並
- 比
X
其他任何對象已被添加到列表(此線程)
這些代碼遭受ABA問題。 – ddoman 2011-10-20 05:39:10
@ddoman:你能否詳細說明你將如何解決它?或者你會只使用維基百科文章中提出的有關ABA問題的方法之一? – Aktau 2013-04-25 22:40:37
我不確定ABA問題是否是一個問題 - 我已更新答案以表明原因。如果我錯了,我會很感激細節。 – 2013-04-26 00:48:18