1. 程式人生 > >4.鎖--無鎖程式設計以及CAS

4.鎖--無鎖程式設計以及CAS

無鎖程式設計 / lock-free / 非阻塞同步

無鎖程式設計,即不使用鎖的情況下實現多執行緒之間的變數同步,也就是在沒有執行緒被阻塞的情況下實現變數的同步,所以也叫非阻塞同步(Non-blocking Synchronization)lock-free是目前最常見的無鎖程式設計的實現級別(一共三種級別)

為什麼要 Non-blocking sync ?

使用lock實現執行緒同步有很多缺點: * 產生競爭時,執行緒被阻塞等待,無法做到執行緒實時響應。 * dead lock。 * live lock。 * 優先順序翻轉。 * 使用不當,造成效能下降。 如果在不使用 lock 的情況下,實現變數同步,那就會避免很多問題。雖然目前來看,無鎖程式設計並不能替代 lock。

實現級別

非同步阻塞的實現可以分成三個級別:wait-free/lock-free/obstruction-free。 wait-free 是最理想的模式,整個操作保證每個執行緒在有限步驟下完成。 保證系統級吞吐(system-wide throughput)以及無執行緒飢餓。 截止2011年,沒有多少具體的實現。即使實現了,也需要依賴於具體CPU。 lock-free 允許個別執行緒飢餓,但保證系統級吞吐。 確保至少有一個執行緒能夠繼續執行。 wait-free的演算法必定也是lock-free的。 obstruction-free 在任何時間點,一個執行緒被隔離為一個事務進行執行(其他執行緒suspended),並且在有限步驟內完成。在執行過程中,一旦發現數據被修改(採用時間戳、版本號),則回滾。 也叫做樂觀鎖,即
樂觀併發控制(OOC)
。事務的過程是:1讀取,並寫時間戳;2準備寫入,版本校驗;3校驗通過則寫入,校驗不通過,則回滾。 lock-free必定是obstruction-free的。

CAS原語

LL/SC, atom read-modify-write 如果CPU提供了Load-Link/Store-Conditional(LL/SC)這對指令,則就可以輕鬆實現變數的CPU級別無鎖同步。 LL [addr],dst:從記憶體[addr]處讀取值到dst。 SC value,[addr]:對於當前執行緒,自從上次的LL動作後記憶體值沒有改變,就更新成新值。 上述過程就是實現lock-free的 read-modify-write 的原子操作。 CAS (Compare-And-Swap)
LL/SC這對CPU指令沒有實現,那麼就需要尋找其他演算法,比如CAS。 CAS是一組原語指令,用來實現多執行緒下的變數同步。 在 x86 下的指令CMPXCHG實現了CAS,前置LOCK既可以達到原子性操作。截止2013,大部分多核處理器均支援CAS。 CAS原語有三個引數,記憶體地址,期望值,新值。如果記憶體地址的值==期望值,表示該值未修改,此時可以修改成新值。否則表示修改失敗,返回false,由使用者決定後續操作。 複製程式碼
Bool CAS(T* addr, T expected, T newValue) 
 { 
      if( *addr == expected ) 
     { 
          *addr =  newValue; 
           return true; 
     } 
     else 
           return false; 
 }
複製程式碼 ABA 問題 thread1意圖對val=1進行操作變成2,cas(*val,1,2)。 thread1先讀取val=1;thread1被搶佔(preempted),讓thread2執行。 thread2 修改val=3,又修改回1。 thread1繼續執行,發現期望值與“原值”(其實被修改過了)相同,完成CAS操作。 使用CAS會造成ABA問題,特別是在使用指標操作一些併發資料結構時。 解決方案 ABAʹ:新增額外的標記用來指示是否被修改。

語言實現

Java demo AtomicInteger atom = new AtomicInteger(1); boolean r = atom.compareAndSet(1, 2); C# demo int i=1; Interlocked.Increment(ref i);

Refs