多線程安全CAS實現的無鎖

實現無鎖隊列的關鍵點有二:

1、各個平臺的原子操作或者說CAS原語;

2、ABA問題的理解和解決。

首先來說說這個CAS原語,所謂CAS(Compare And Swap)即比較並交換,在 Intel 處理器中,比較並交換通過指令的 cmpxchg 系列實現。CAS有三個操作數: 內存位置(V)、預期原值(A)和新值(B)。如果內存位置V的值與預期A原值相匹配,那麼處理器會自動將該位置值更新爲新值B。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前值。)CAS 有效地說明了“我認爲位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”CAS的C語言實現如下:

inline bool CAS2(pointer_t *addr, pointer_t &old_value, pointer_t &new_value)
{
bool ret;
__asm__ __volatile__(
"lock cmpxchg16b %1;\n"
"sete %0;\n"
:"=m"(ret),"+m" (*(volatile pointer_t *) (addr))
:"a" (old_value.ptr), "d" (old_value.tag), "b" (new_value.ptr), "c" (new_value.tag));
return ret;
}

這其中包括了內聯彙編代碼(這個可以去看看AT&T彙編語法),其中能夠支持多線程的並行安全執行的祕訣就在 "lock cmpxchg16b %1;\n"這句中的“lock”,這個lock就和我們基本多線程編程中的鎖相當,只不過,這個lock不再是普通的鎖,它鎖的是地址總線,在多核編程情景下,當已經有CPU的線程A已經訪問某地址時,這時地址總線會被鎖定,其他CPU核心的線程B無法再訪問當前地址,直到A訪問完畢之後,其他線程如B纔可能訪問該地址,這樣就保證了線程A訪問該地址的原子性操作。

再來講講這個ABA問題。在進行CAS操作時,因爲在更改V值之前,CAS主要是通過訪問V的值是否仍然和A相等,所以,在第一次讀取V以及對V執行CAS操作之前,如果有其他線程將V的值先從A改爲B,而另外的線程又將V的值從B改回了A,這樣會使CAS算法混亂。顯然,在這種情況下,CAS的操作會成功,這類的問題稱爲ABA問題。要解決這類問題,就是不再重用A,通常的做法是用標記或版本編號與進行CAS操作的每個值相關聯,並原子的更新值和標記。


CAS實現的無鎖

#include <stdio.h>

    #include <stdlib.h>

    #include <pthread.h>

    #include <unistd.h>

    #include <time.h>

    #include "timer.h"

   

    int mutex = 0;

    int lock = 0;

    int unlock = 1;

  

   static volatile int count = 0;

   void *test_func(void *arg)

   {

          int i = 0;

          for(i = 0; i < 2000000; i++)

         {

          while (!(__sync_bool_compare_and_swap (&mutex,lock, 1) ))usleep(100000);

            count++;

            __sync_bool_compare_and_swap (&mutex, unlock, 0);

          }

           return NULL;

   }

    

   int main(int argc, const char *argv[])

   {

      Timer timer;

      timer.Start();

       pthread_t thread_ids[10];

       int i = 0;

    

      for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++)

      {

              pthread_create(&thread_ids[i], NULL, test_func, NULL);

      }

   

       for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++)

       {

               pthread_join(thread_ids[i], NULL);

       }

    

       timer.Stop();

       timer.Cost_time();

       printf("結果:count = %d\n",count);

  

       return 0;

   }


發佈了99 篇原創文章 · 獲贊 26 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章