linux編程的108種奇淫巧計-7(Lock-free實驗)

來源:互聯網
上載者:User

      從該部落格開始,會有一些小系列預計有4-5篇博文來介紹,鎖的應用和實踐,我們常常聽到spin lock,wait-free,lock-free,這到底是怎麼回事,我們能不能自己實現一個spin lock,原理是什嗎?這個小系列就討論這個內容。

 

      首先我們來看兩個基本操作compare_and_swap和fetch_and_add,基本上lock-free的操作都會依賴這兩個基本的原子操作。特別是compare_and_swap這個原子操作,它源於IBM System 370,其包含三個參數:(1)共用記憶體的地址(*p),(2)該地址期望的值(old_value),(3)一個新值(new_value)。只有當*p == old_value時,才產生交換操作,返回真值,否則返回假值,相當於如下代碼 :template<class T>

        bool CAS(T* addr, T exp, T val) //只有在整個函數過程具有原子性時才正確,實際的代碼參照下面的彙編代碼。
      {
              if(*addr == exp){
                                             *addr = val;
                                              return true;
                }
              return false;
      }

      在下面的代碼中我們會看到compare_and_swap使用了lock指令,用於鎖匯流排,setz會判斷cmpxchg指令後ZF符號位是否置位,可以知道是否發生了一次交換。以下是一段可以執行的代碼,void* sum(void*)函數通過不同的編譯命令產生不同的代碼,其結果都是用10個線程對一個全域變數進行加和的簡單操作。但分別採用了pthread提供的mutex,fetch_and_add方法,完全無鎖的方法,應用cas的三種方式,其中sum_with_cas_imp_yield就基本是spinlock的實現了。

      下一篇我來公布在我的測試機的實驗結果,並且繼續探討其他lock-free的話題。

 

 

#include <pthread.h>

#include <stdio.h>

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <syscall.h>
#if defined(__x86_64__) 
        #define ATOMICOPS_WORD_SUFFIX "q"  //64位環境下使用cmpxchgq命令
#else
        #define ATOMICOPS_WORD_SUFFIX "l"   //32位環境下使用cmpxchgl命令
#endif
static inline bool compare_and_swap(volatile size_t *p, size_t val_old, size_t val_new){
        char ret;
        __asm__ __volatile__("lock; cmpxchg" ATOMICOPS_WORD_SUFFIX " %3, %0; setz %1"//lock命令鎖匯流排,因此可以保證多核同步
                        : "=m"(*p), "=q"(ret)      //setz為ZF符號位是否置位,用於設定傳回值
                        : "m"(*p), "r" (val_new), "a"(val_old)
                        : "memory");
        return (bool)ret;
}
static inline size_t fetch_and_add(volatile size_t *p, size_t add){
        unsigned int ret;
        __asm__ __volatile__("lock; xaddl %0, %1"
                        :"=r" (ret), "=m" (*p)
                        : "0" (add), "m" (*p)
                        : "memory");
        return ret;
};
struct my_cas
{
        my_cas(unsigned char t):m_val_old(t){}
        size_t m_val_old;
        inline void try_continue(size_t val_old,size_t val_new){
                while(!compare_and_swap(&m_val_old,val_old,val_new)){};
        }
inline void add(size_t val_new){
                fetch_and_add(&m_val_old,val_new);
        }
};
volatile size_t g_uCount;
pthread_mutex_t g_tLck=PTHREAD_MUTEX_INITIALIZER;
my_cas mutex(1);
const size_t cnt_num = 10000000;
void* sum_with_mutex_lock(void*)
{
        for(int i=0;i < cnt_num;++i) {
                pthread_mutex_lock(&g_tLck);
                g_uCount += 1;
                pthread_mutex_unlock(&g_tLck);
        }
};
void* sum_with_f_and_a(void*)  //用fetch_and_add原子操作來保證結果正確性。
{
        for(int i=0;i < cnt_num;++i) {
                fetch_and_add(&g_uCount,1);
        }
};
void* sum_with_cas(void*)  //用CAS原子操作來類比鎖操作。
{      
        for(int i=0;i< cnt_num;++i)
        {      
                mutex.try_continue(1,0);
                g_uCount += 1;
                mutex.try_continue(0,1);  
        }
}
void* sum_with_cas_imp(void*)
{
        for(int i=0;i< cnt_num;++i) {
                for(;;) {
                        size_t u = g_uCount;
                        if(compare_and_swap(&g_uCount,u,u+1)){   //在上一條語句和本條語句之間,g_uCount無篡改則進行加1,
                                break;        //break出該迴圈,否則重試,直到成功。
                        }
                }
        }
}
void* sum_with_cas_imp_yield(void*)
{
        for(int i=0;i< cnt_num;++i) {
                for(;;) {
                        register size_t c = 1000; //
                        while(c){
                                size_t u = g_uCount;
                                if(compare_and_swap(&g_uCount,u,u+1)){
                                        break;
                                }
                                c--;
                        }
                        if(!c){
                                syscall(SYS_sched_yield); //增加一次讓渡CPU的機會,spin lock通常應有這種策略
                        }
                }
        }
}
void* sum_just_free(void*)
{      
        for(int i=0;i < cnt_num;++i) {  //完全無鎖,無等待,但執行結果通常是錯誤的。
                g_uCount += 1;
        }
}
void* sum(void*)
{
        #ifdef M_LOCK
         sum_with_mutex_lock(NULL);
        #endif
        #ifdef FETCH_AND_ADD
         sum_with_f_and_a(NULL);
        #endif
        #ifdef FREE
         sum_just_free(NULL);
        #endif
        #ifdef CAS
         sum_with_cas(NULL);
        #endif
        #ifdef CAS_IMP
         sum_with_cas_imp(NULL);
        #endif
#ifdef CAS_IMP_YIELD
         sum_with_cas_imp_yield(NULL);
        #endif
};
int main()
{      
        pthread_t* thread = (pthread_t*) malloc(10*sizeof( pthread_t));
        for(int i=0;i<10;++i){      
                pthread_create(&thread[i],NULL,sum,NULL);
        }
        for(int i=0;i<10;++i){      
                pthread_join(thread[i],NULL);
        }
        printf("g_uCount:%d/n",g_uCount);
}
用以下編譯命令編譯出6個程式
g++ test.cpp -o test_free -D FREE -lpthread
g++ test.cpp -o test_fetchandadd -D FETCH_AND_ADD -lpthread
g++ test.cpp -o test_mlock -D M_LOCK -lpthread
g++ test.cpp -o test_cas -D CAS -lpthread
g++ test.cpp -o test_cas_imp -D CAS_IMP –lpthread
g++ test.cpp –o test_cas_imp_yield –D CAS_IMP_YIELD -lpthread

 

本系列其他文章:http://blog.csdn.net/pennyliang/category/746545.aspx

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.