無鎖編程關鍵點.看了下,作個筆記.
無鎖數據依靠的是CAS.即比較置
與比較交換
.
參考1
參考2
比較交換的實現:
int compare_and_swap (int* reg, int oldval, int newval)
{
ATOMIC();
int old_reg_val = *reg;
if (old_reg_val == oldval)
*reg = newval;
END_ATOMIC();
return old_reg_val;
}
看見沒有,原子操作,要先有個舊對象,然後相互比較,即看看數據變沒有,數據沒變即進行新值替換
這裏比平常多一步判定原先數據變化的操作.
因此,這也是碰運氣.如果數據,比如指針,看起來沒變化,但實質上,指針的內容已經變化(釋放並重用
)了,這就是漏洞的根源
了.所以,無鎖編程,就是要解決這個問題,即如何判定這一段時間,數據是否發生了變化.
接下來幾個比較交換弱,比較交換強
,找cpp參考的原子操作.
僞碼:
if *this == expected:
*this = desired;
else:
expected = *this;
仔細觀察如下代碼:
if ( head == new_node->new){//數據沒變
head = new_node;//直接壓入數據
return true;//成功.
}else{//數據變化
new_node->next = head;//頭已變化,得跟着變化
return false;//此次失敗,下次再來
}//這是壓,彈沒寫清楚.
//下面還有.
現在幾乎所有的CPU指令都支持CAS的原子操作,X86下對應的是 CMPXCHG 彙編指令。
無鎖隊列的鏈表實現
:
EnQueue(x) //進隊列
{
//準備新加入的結點數據
q = new record();
q->value = x;
q->next = NULL;
do {
p = tail; //取鏈表尾指針的快照
} while( CAS(p->next, NULL, q) != TRUE); //如果沒有把結點鏈在尾指針上,再試
//原子比較,這一步比較若爲相同,即未被處理,
CAS(tail, p, q); //置尾結點
//原子加.是兩個相鄰原子操作.
}
無鎖編程,其實就是看待添加位置是否被佔.已佔,重新調整待添加位置.然後多個線程不斷循環操作.只要判斷位置是否被佔與添加尾結點,這兩步是原子操作.不然,就是一片混亂了.所以,所謂的無鎖編程就是搶佔法.只在不得不排外的情況下,進行原子操作.
改良版,萬一當前線程壞了,我覺得太多此一舉了.沒必要,或者只2次,也麻煩.也搞不懂.見參考2.
好,下面是出列:
DeQueue() //出隊列
{
do{
p = head;
if (p->next == NULL){
return ERR_EMPTY_QUEUE;
}
}while( CAS(head, p, p->next) != TRUE );//陳大神這裏少個}.一樣的,比較時變化了.在這一步裏面進行操作.如果爲真,代表操作成功,即彈出去了.否則,移動當前指針.
return p->next->value;//返回數據
}
所謂的ABA就是我上面說的,你看起來沒變的東西,實質上已經變了.就像指針,主要還是那個問題:如何判定是否已被佔?
無鎖編程,總之,就是一個搶佔問題.將鎖的粒度控制在最小.無關的排外都去掉了.