亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

利用C++11原子量如何實現自旋鎖詳解

2020-05-23 13:26:53
字體:
來源:轉載
供稿:網友

一、自旋鎖

自旋鎖是一種基礎的同步原語,用于保障對共享數據的互斥訪問。與互斥鎖的相比,在獲取鎖失敗的時候不會使得線程阻塞而是一直自旋嘗試獲取鎖。當線程等待自旋鎖的時候,CPU不能做其他事情,而是一直處于輪詢忙等的狀態。自旋鎖主要適用于被持有時間短,線程不希望在重新調度上花過多時間的情況。實際上許多其他類型的鎖在底層使用了自旋鎖實現,例如多數互斥鎖在試圖獲取鎖的時候會先自旋一小段時間,然后才會休眠。如果在持鎖時間很長的場景下使用自旋鎖,則會導致CPU在這個線程的時間片用盡之前一直消耗在無意義的忙等上,造成計算資源的浪費。

使用自旋鎖時要注意:

由于自旋時不釋放CPU,因而持有自旋鎖的線程應該盡快釋放自旋鎖,否則等待該自旋鎖的線程會一直在哪里自旋,這就會浪費CPU時間。

持有自旋鎖的線程在sleep之前應該釋放自旋鎖以便其他咸亨可以獲得該自旋鎖

二、CAS操作實現自旋鎖

CAS(Compare and Swap),即比較并替換,實現并發算法時常用到的一種技術,這種操作提供了硬件級別的原子操作(通過鎖總線的方式)。CAS操作的原型可以認為是:

bool CAS(V, A, B)

其中V代表內存中的變量,A代表期待的值,B表示新值。當V的值與A相等時,將V與B的值交換。邏輯上可以用下面的偽代碼表示:

bool CAS(V, A, B){ if (V == A) { swap(V, B); return true; }  return false;}

需要強調的是上面的操作是原子的,要么不做,要么全部完成。

那么已經擁有CAS操作的情況下如何實現一個自旋鎖呢?首先回憶自旋鎖的用途,本質上我們是希望能夠讓一個線程在不滿足進入臨界區的條件時,不停的忙等輪詢,直到可以運行的時候再繼續(進入臨界區)執行。那么,我們可能自然的想到使用一個bool變量來表示是否可以進入臨界區,例如以下面的偽代碼的邏輯:

while(flag == true);flag = true;/*do something ...*/flag = false; ...

這樣做的直觀想法是當flag為true的時候表示已經有線程處于臨界區內,只有當flag為fasle時才能進入,而在進入的時候立即將flag置為true。但是這樣做明顯存在一個問題,判斷flag為false和設置flag為true并不是一個不可分割的整體,有可能出現類似下面這樣的時序, 假設最初flag為false:

 

step thread 1 thread 2
1 while(flag == true);  
2   while(flag == true);
3 flag = true  
4   flag = true
5 do something do something
6   flag = false
7 flag = false  

 

step是虛構的步驟,do something為一系列指令,這里寫在一起表示并發執行。這里可以看出由于thread1讀取判斷flag的值與修改flag的值是兩個獨立的操作,中間插入了thread2的判斷操作,最終使得有兩個線程同時進入了臨界區,這與我們的期望相悖。那么如何解決呢?如果能將讀取判斷與修改的操作合二為一,變成一個不可分割的整體,那么自然就不可能出現這種交錯的場景。對于這樣一個整體操作,我們希望它能讀取內存中變量的值,并且當其等于特定值的時候,修改它為我們需要的另一個值。嗯......沒錯,這樣我們就得到了CAS操作。

現在可以重新修改我們的同步方式,不停的進行期望flag為false的CAS操作 CAS(flag, flase, b) (這里b為true),直到其返回成功為止,再進行臨界區中的操作,離開臨界區時將flag置為false。

b = true;while(!CAS(flag, false, b));//do somethingflag = false;

現在,判斷操作與寫入操作已經成為了一個整體,當一個線程的CAS操作成功的時候會阻止其他線程進入臨界區,到達互斥訪問的目的。

現在我們已經可以使用CAS操作來解決臨界區的互斥訪問的問題了,但是如果每次都這樣寫一遍實在太過麻煩,因此可以進行一些封裝使得使用更加方便,也就是說...可以封裝成自旋鎖。我們可以用一個類來表示,將一個bool值作為類的數據成員,同時將CAS操作和賦值操作作為其成員函數,CAS操作其實就是加鎖操作,而后面的賦值操作就是解鎖操作。

三、用C++原子量實現

按照上面的思路,接下來用 C++ 11 引入標準庫的原子量來實現一個自旋鎖并且進行測試。

首先,我們需要一個bool值來表示鎖的狀態,這里直接使用標準庫中的原子量 atomic<bool> (C++ 11的原子量可以參考:http://www.49028c.com/kaifa/c/321701.html ,在我的平臺(Cygwin64、GCC7.3)上 atomic<bool> 的成員函數is_lock_free()返回值為true,是無鎖的實現(如果內部使用了鎖來實現的話那還叫什么自旋鎖 = =)。實際上在大多數平臺上 atomic<bool> 都是無鎖的,如果不確定的話也可以使用C++標準規定必須為無鎖實現的atomic_flag。

接下來,我們需要兩個原子操作,CAS和賦值,C++11標準庫在原子量的成員函數中直接提供了這兩個操作。

//CASstd::atomic::compare_exchange_weak( T& expected, T desired,     std::memory_order order =     std::memory_order_seq_cst ),     std::atomic::compare_exchange_strong( T& expected, T desired,     std::memory_order order =     std::memory_order_seq_cst )//賦值void store( T desired, std::memory_order order = std::memory_order_seq_cst )

compare_exchange_weak 與 compare_exchange_strong 主要的區別在于內存中的值與expected相等的時候,CAS操作是否一定能成功,compare_exchange_weak有概率會返回失敗,而compare_exchange_strong則一定會成功。因此,compare_exchange_weak必須與循環搭配使用來保證在失敗的時候重試CAS操作。得到的好處是在某些平臺上compare_exchange_weak性能更好。按照上面的模型,我們本來就要和while搭配使用,可以使用compare_exchange_weak。最后內存序的選擇沒有特殊需求直接使用默認的std::memory_order_seq_cst。而賦值操作非常簡單直接,這個調用一定會成功(只是賦值而已 = =),沒有返回值。

實現代碼非常短,下面是源代碼:

#include <atomic>class SpinLock {public: SpinLock() : flag_(false) {} void lock() { bool expect = false; while (!flag_.compare_exchange_weak(expect, true)) {  //這里一定要將expect復原,執行失敗時expect結果是未定的  expect = false; } } void unlock() { flag_.store(false); }private: std::atomic<bool> flag_;};

如上面所說,lock操作不停的嘗試CAS操作直到成功為止,unlock操作則將bool標志位復原。使用方式如下:

SpinLock myLock;myLock.lock();//do somethingmyLock.unlock();

接下來,我們進行正確性測試,以經典的i++ 問題為例:

#include <atomic>#include <thread>#include <vector>//自旋鎖類定義class SpinLock {public: SpinLock() : flag_(false) {} void lock() { bool expect = false; while (!flag_.compare_exchange_weak(expect, true)) {  expect = false; } } void unlock() { flag_.store(false); }private: std::atomic<bool> flag_;};//每個線程自增次數const int kIncNum = 1000000;//線程數const int kWorkerNum = 10;//自增計數器int count = 0;//自旋鎖SpinLock spinLock;//每個線程的工作函數void IncCounter(){ for (int i = 0; i < kIncNum; ++i) { spinLock.lock(); count++; spinLock.unlock(); }}int main(){ std::vector<std::thread> workers; std::cout << "SpinLock inc MyTest start" << std::endl; count = 0; std::cout << "start " << kWorkerNum << " workers_" << "every worker inc " << kIncNum << std::endl; std::cout << "count_: " << count << std::endl; //創建10個工作線程進行自增操作 for (int i = 0; i < kWorkerNum; ++i) workers.push_back(std::move(std::thread(IncCounter))); for (auto it = workers.begin(); it != workers.end(); it++) it->join(); std::cout << "workers_ end" << std::endl; std::cout << "count_: " << count << std::endl; //驗證結果 if (count == kIncNum * kWorkerNum) { std::cout << "SpinLock inc MyTest passed" << std::endl; return true; } else { std::cout << "SpinLock inc MyTest failed" << std::endl; return false; } return 0;}

上面的代碼中創建了10個線程對共享的全局變量count分別進行一百萬次++操作,然后驗證結果是否正確,最終執行的輸出為:

SpinLock inc MyTest startstart 10 workers_every worker inc 1000000count_: 0workers_ endcount_: 10000000SpinLock inc MyTest passed

從結果中可以看出我們實現的自旋鎖起到了保護臨界區(這里就是i++ )的作用,count最后的值等于每個線程執行自增的數目之和。作為對比,可以去掉IncCounter中的加鎖解鎖操作:

void IncCounter(){ for (int i = 0; i < kIncNum; ++i) { //spinLock.lock(); count++; //spinLock.unlock(); }}

執行后的輸出為:

SpinLock inc MyTest startstart 10 workers_every worker inc 1000000count_: 0workers_ endcount_: 7254522SpinLock inc MyTest failed

結果由于多個線程同時執行 i++ 造成結果錯誤。

到這里,我們就通過 C++ 11的原子量實現了一個簡單的自旋鎖。這里只是對C++原子量的一個小使用,無論是自旋鎖本身還是原子量都還有許多值得探究的地方。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品免费电影| 亚洲色图色老头| 国产成人啪精品视频免费网| 久久国产精品偷| 日韩av免费网站| 91精品91久久久久久| 成人欧美在线观看| 亚洲一区二区三区xxx视频| 成人h片在线播放免费网站| 69av在线视频| 国产日韩欧美在线视频观看| 日韩第一页在线| 久久久精品一区二区三区| 奇米一区二区三区四区久久| 国产精品中文字幕在线| 中国日韩欧美久久久久久久久| 高清一区二区三区日本久| 亚洲最大激情中文字幕| 欧美日韩国产影院| 91沈先生在线观看| 精品综合久久久久久97| 日韩精品视频中文在线观看| 在线观看久久久久久| 亚洲精品一区在线观看香蕉| 日韩亚洲欧美中文在线| 久久久亚洲精品视频| 欧美夫妻性生活xx| 日韩资源在线观看| 国产欧美精品xxxx另类| 综合国产在线视频| 国产精品欧美一区二区三区奶水| 91精品国产91久久久久久不卡| 亚洲精品免费一区二区三区| 亚洲高清不卡av| 日韩电影在线观看永久视频免费网站| 久久人人爽人人爽爽久久| 红桃视频成人在线观看| 日韩高清免费在线| 欧美极品xxxx| 国产成+人+综合+亚洲欧洲| 日韩免费视频在线观看| 黑人与娇小精品av专区| 97精品在线视频| 欧美限制级电影在线观看| 免费91麻豆精品国产自产在线观看| 欧美激情一区二区三级高清视频| 国产精品亚洲综合天堂夜夜| 亚洲国产精品电影在线观看| 国产日韩av高清| 日本不卡免费高清视频| 日韩av在线资源| 成人亲热视频网站| 91精品久久久久久久久久久久久| 成人网中文字幕| 欧美裸身视频免费观看| 亚洲欧洲一区二区三区久久| 欧美日韩免费一区| 黑人与娇小精品av专区| 欧美激情免费看| 欧美成人网在线| 国产做受高潮69| 久久久久久中文字幕| 永久免费毛片在线播放不卡| 91成人性视频| 黑人巨大精品欧美一区二区一视频| 日韩欧美第一页| 欧美日韩精品中文字幕| 在线播放国产一区二区三区| 2018国产精品视频| 精品久久久久人成| 日韩激情av在线免费观看| 欧美激情喷水视频| 久久99久国产精品黄毛片入口| 91九色精品视频| 欧美性猛交xxxx免费看| 精品无人国产偷自产在线| 中文字幕亚洲在线| 日韩国产欧美区| 九九热这里只有精品免费看| 疯狂蹂躏欧美一区二区精品| 国语自产在线不卡| 国产日韩精品入口| 国产精品久久久久久久久久久久久久| 久久久久这里只有精品| 欧美日韩人人澡狠狠躁视频| 成人444kkkk在线观看| 日韩成人中文字幕在线观看| 中文字幕日韩欧美在线| 欧美成人合集magnet| 久久久99久久精品女同性| 国产美女被下药99| 久久久久久久久久久亚洲| 色婷婷av一区二区三区久久| 日av在线播放中文不卡| 日韩精品视频中文在线观看| 欧美日韩国产一中文字不卡| 日韩在线视频中文字幕| 国外成人性视频| 亚洲xxxx视频| 国产久一一精品| 久久97久久97精品免视看| 亚洲免费成人av电影| 亚洲欧美一区二区三区四区| 日韩精品免费一线在线观看| 亚洲国产精品国自产拍av秋霞| 亚洲一区二区在线| 国产拍精品一二三| 亚洲一区二区三区777| 久久精品成人欧美大片| 国产成人精品av| 国产色婷婷国产综合在线理论片a| 欧美国产日韩在线| 国产区精品在线观看| 久久国内精品一国内精品| 久久国产一区二区三区| 精品夜色国产国偷在线| 国产一区二区三区丝袜| 国产精品自拍偷拍| www亚洲精品| 日韩一区二区三区在线播放| 欧美久久精品午夜青青大伊人| 久久99亚洲热视| 日韩精品中文字幕有码专区| 自拍偷拍亚洲区| 国产91精品久久久| 欧美高清videos高潮hd| 欧美激情三级免费| 色播久久人人爽人人爽人人片视av| 青青在线视频一区二区三区| 亚洲电影免费在线观看| 国产精品成久久久久三级| 国内久久久精品| 欧美韩国理论所午夜片917电影| 国产精选久久久久久| 日韩精品在线看| 欧美日韩一区二区精品| 久久综合色88| 一道本无吗dⅴd在线播放一区| 日韩精品高清视频| 91精品国产色综合久久不卡98口| 欧美亚洲国产另类| 国内免费精品永久在线视频| 国产精品视频一区二区三区四| 26uuu日韩精品一区二区| 欧美性高跟鞋xxxxhd| zzjj国产精品一区二区| 17婷婷久久www| 庆余年2免费日韩剧观看大牛| 欧美激情影音先锋| 亚洲精品狠狠操| 欧美激情乱人伦| 亚洲第一国产精品| 成人国产精品日本在线| 亚洲天堂久久av| 日韩精品极品视频| 欧美视频在线观看 亚洲欧| 中文字幕日韩欧美在线视频| 亚洲免费影视第一页| 国产成人亚洲综合91精品| 国产精品日韩专区| 国产精品福利久久久| 欧美黑人狂野猛交老妇| 亚洲最大在线视频| 午夜精品福利视频|