做這樣幾乎不可能的事情的關鍵是使用InterlockedCompareExchange。(這是Win32使用的名稱,但任何支持多線程的平臺都會有InterlockedCompareExchange等效項)。
的想法是,你做出結構的副本(必須足夠小,以執行單位讀取(64或如果你能處理一些unportability,在x86上128位)。
你讓另一個副本與你的建議的更新,做你的邏輯和更新的副本,那麼您在使用InterlockedCompareExchange更新「真實」的結構。什麼InterlockedCompareExchange所做的是,原子確保該值仍是您開始使用狀態更新前的值,如果它是還是那個值,原子更新與新狀態的值通常這被包裝在一個無限循環不停地嘗試,直到別人沒有改變在中間值以下是大致的模式:。
union State
{
struct
{
short a;
short b;
};
uint32_t volatile rawState;
} state;
void DoSomething()
{
// Keep looping until nobody else changed it behind our back
for (;;)
{
state origState;
state newState;
// It's important that you only read the state once per try
origState.rawState = state.rawState;
// This must copy origState, NOT read the state again
newState.rawState = origState.rawState;
// Now you can do something impossible to do atomically...
// This example takes a lot of cycles, there is huge
// opportunity for another thread to come in and change
// it during this update
if (newState.b == 3 || newState.b % 6 != 0)
{
newState.a++;
}
// Now we atomically update the state,
// this ONLY changes state.rawState if it's still == origState.rawState
// In either case, InterlockedCompareExchange returns what value it has now
if (InterlockedCompareExchange(&state.rawState, newState.rawState,
origState.rawState) == origState.rawState)
return;
}
}
(請原諒,如果上面的代碼不實際編譯 - 我寫了我的頭頂部)
大。現在,您可以輕鬆實現無鎖算法。錯誤!麻煩的是,您可以自動更新的數據量受到嚴格限制。
一些無鎖算法使用的技術,他們「幫助」併發線程。例如,假設你有一個鏈接列表,你希望能夠從多個線程中更新,其他線程可以通過執行「第一個」和「最後一個」指針的更新來「幫助」,如果他們正在通過並看到它們是在由「last」指向的節點處,但節點中的「next」指針不爲null。在這個例子中,當注意到「最後一個」指針是錯誤的時,它們會更新最後一個指針,只要它仍然指向當前節點,使用互鎖比較交換。
不要落入陷阱,你「旋轉」或環(如自旋鎖)。雖然短暫旋轉是有價值的,因爲你期望「其他」線程完成某些事情 - 它們可能不會。 「其他」線程可能已被上下文切換,並且可能不再運行。你只是在吃CPU時間,燃燒電力(可能殺死一臺筆記本電腦電池),直到情況成真。當你開始旋轉的時候,你不妨鎖定你的無鎖代碼並用鎖來寫。鎖比無限旋轉要好。想要從艱難到荒謬的過程中,考慮一下你可以使用其他體系結構的混亂情況 - 在x86/x64上,一般都很寬容,但是當你進入其他「弱排序」體系結構時,你會進入領域事情發生的地方沒有任何意義 - 內存更新不會按照程序順序進行,因此,所有關於其他線程正在做什麼的心理推理都會消失。 (即使是在x86/x64有一個名爲它經常被用來更新顯存,但可用於任何內存緩衝區的硬件,你需要圍欄時「寫入合併」的存儲器類型)的架構要求您使用「記憶柵欄」操作,以保證所有讀/寫/兩者都將在全局可見的情況下(由其他內核執行)。寫入柵欄保證柵欄之前的任何寫入在柵欄之後的任何寫入之前將全局可見。讀圍欄將保證在圍欄之前沒有讀數會在圍欄之前被推測性地執行。讀/寫柵欄(又名全柵欄或內存柵欄)將作出保證。柵欄非常昂貴。 (有些使用術語「屏障」,而不是「籬笆」)
我的建議是用鎖/條件變量第一個實現它。如果你在完成這項工作時遇到任何問題,那麼嘗試執行無鎖實現是沒有希望的。並始終衡量,衡量,衡量。您可能會發現使用鎖的實現的性能非常好 - 沒有一些片狀無鎖實現的不確定性,只有當您向重要客戶進行演示時纔會顯示natsy掛起錯誤。也許你可以通過將原始問題重新定義爲更容易解決的問題來解決這個問題,也許可以通過重組工作來讓更大的物品(或批次物品)進入集合,從而減輕整個事物的壓力。編寫無鎖定的併發算法非常困難(正如你在其他地方寫過的,我確信)。這通常也是不值得的。
我並不想過於嚴格,但如果您對鎖定免費數據結構非常認真,那麼您應該查找所有可以找到的鏈接。 Deque並不是一個容易開始的地方,誠實地說,從這個外觀來看,您可能想要回到更簡單的數據結構並開始工作。從這裏開始:http://www.boyet.com/articles/lockfreestack.html有一整套系列。 – 2009-12-02 20:46:40