無鎖編程關鍵點

無鎖編程關鍵點.看了下,作個筆記.
無鎖數據依靠的是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就是我上面說的,你看起來沒變的東西,實質上已經變了.就像指針,主要還是那個問題:如何判定是否已被佔?
無鎖編程,總之,就是一個搶佔問題.將鎖的粒度控制在最小.無關的排外都去掉了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章