1. 程式人生 > >多執行緒的那點兒事(之原子鎖)

多執行緒的那點兒事(之原子鎖)

                【 宣告:版權所有,歡迎轉載,請勿用於商業用途。  聯絡信箱:feixiaoxing @163.com】    原子鎖是多執行緒程式設計中的一個特色。然而,在平時的軟體編寫中,原子鎖的使用並不是很多。這其中原因很多,我想主要有兩個方面。第一,關於原子鎖這方面的內容介紹的比較少;第二,人們在程式設計上面習慣於已有的方案,如果沒有特別的需求,不過貿然修改已存在的程式碼。畢竟對很多人來說,不求有功,但求無過。保持當前程式碼的穩定性還是很重要的。      其實,早在《多執行緒資料互斥》這篇部落格中,我們就已經介紹過原子鎖。本篇部落格主要討論的就是原子鎖怎麼使用。中間的一些用法只是我個人的一些經驗,希望能夠拋磚引玉,多聽聽大家的想法。

    (1)查詢函式中原子鎖    

    在一些函式當中,有的時候我們需要對滿足某種特性的資料進行查詢。在傳統的單核CPU上,優化的空間比較有限。但是,現在多核CPU已經成了主流配置。所以我們完全可以把這些查詢工作分成幾個子函式分在幾個核上面並行運算。但是,這中間就會涉及到一個問題,那就是對公共資料的訪問。傳統的訪問方式,應該是這樣的,

unsigned int count = 0;int find_data_process()
{    if(/* data meets our standards */){         EnterCriticalSection(&cs);         count ++;         LeaveCriticalSection(&cs);             }}
    我們看到程式碼中間使用到了鎖,那麼勢必會涉及到系統呼叫和函式排程。所以,在執行效率上會大打折扣。那麼如果使用原子鎖呢?
unsigned int count = 0;int find_data_process(){    if(/* data meets our standards */){        InterLockedIncrement(&count);    }}

    有興趣的朋友可以做這樣一道題目,檢視0~0xFFFFFFFF上有多少數可以被3整除?大家也可以驗證一下用原子鎖代替臨界區之後,程式碼的效率究竟可以提高多少。關於多核多執行緒的程式設計,朋友們可以參考《多執行緒基礎篇》這篇部落格。

    (2)程式碼段中的原子鎖    上面的範例只是介紹了統計功能中的原子鎖。那麼怎麼用原子鎖代替傳統的系統鎖呢?比如說,假設原來的資料訪問是這樣的,

void data_process(){    EnterCriticalSection(&cs);    do_something();    LeaveCriticalSection(&cs);   }
    如果改成原子鎖呢,會是什麼樣的呢?
unsigned int lock = 0;void data_process(){    while(1 == InterLockedCompareExchange(&lock, 1, 0));    do_something();    lock = 0;    }

    這裡用原子鎖代替普通的系統鎖,完成的功能其實是一樣的。那麼這中間有什麼區別呢?其實,關鍵要看do_something要執行多久。打個比方來說,現在我們去買包子,但是買包子的人很多。那怎麼辦呢?有兩個選擇,如果賣包子的人手腳麻利,服務一個顧客只要10秒鐘,那麼即使前面排隊的有50個人,我們只要等7、8分鐘就可以,這點等的時間還是值得的;但是如果不幸這個賣包子的老闆服務一個顧客要1分鐘,那就悲催了,假使前面有50個人,那我們就要等50多分鐘了。50分鐘對我們來說可是不短的一個時間,我們完全可以利用這個時間去買點水果,交交水電費什麼的,過了這個時間點再來買包子也不遲。

    和上面的例子一樣,忙等的方法就是原子鎖,過一會再來的方法就是哪個傳統的系統鎖。用哪個,就看這個do_something的時間值不值得我們等待了。