無鎖編程以及CAS


無鎖編程 / lock-free / 非阻塞同步

無鎖編程,即不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量的同步,所以也叫 非阻塞同步(Non-blocking Synchronization)
實現非阻塞同步的方案稱為“無鎖編程算法”(  Non-blocking algorithm)。
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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM