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

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

C++多線程編程時的數據保護

2020-01-26 15:02:14
字體:
來源:轉載
供稿:網友

 在編寫多線程程序時,多個線程同時訪問某個共享資源,會導致同步的問題,這篇文章中我們將介紹 C++11 多線程編程中的數據保護。
數據丟失

讓我們從一個簡單的例子開始,請看如下代碼:
 

#include <iostream>#include <string>#include <thread>#include <vector> using std::thread;using std::vector;using std::cout;using std::endl; class Incrementer{  private:    int counter;   public:    Incrementer() : counter{0} { };     void operator()()    {      for(int i = 0; i < 100000; i++)      {        this->counter++;      }    }     int getCounter() const    {      return this->counter;    }   }; int main(){  // Create the threads which will each do some counting  vector<thread> threads;   Incrementer counter;   threads.push_back(thread(std::ref(counter)));  threads.push_back(thread(std::ref(counter)));  threads.push_back(thread(std::ref(counter)));   for(auto &t : threads)  {    t.join();  }   cout << counter.getCounter() << endl;   return 0;}

這個程序的目的就是數數,數到30萬,某些傻叉程序員想要優化數數的過程,因此創建了三個線程,使用一個共享變量 counter,每個線程負責給這個變量增加10萬計數。

這段代碼創建了一個名為 Incrementer 的類,該類包含一個私有變量 counter,其構造器非常簡單,只是將 counter 設置為 0.

緊接著是一個操作符重載,這意味著這個類的每個實例都是被當作一個簡單函數來調用的。一般我們調用類的某個方法時會這樣 object.fooMethod(),但現在你實際上是直接調用了對象,如object(). 因為我們是在操作符重載函數中將整個對象傳遞給了線程類。最后是一個 getCounter 方法,返回 counter 變量的值。

再下來是程序的入口函數 main(),我們創建了三個線程,不過只創建了一個 Incrementer 類的實例,然后將這個實例傳遞給三個線程,注意這里使用了 std::ref ,這相當于是傳遞了實例的引用對象,而不是對象的拷貝。

現在讓我們來看看程序執行的結果,如果這位傻叉程序員還夠聰明的話,他會使用 GCC 4.7 或者更新版本,或者是 Clang 3.1 來進行編譯,編譯方法:
 

g++ -std=c++11 -lpthread -o threading_example main.cpp

運行結果:

 

[lucas@lucas-desktop src]$ ./threading_example218141[lucas@lucas-desktop src]$ ./threading_example208079[lucas@lucas-desktop src]$ ./threading_example100000[lucas@lucas-desktop src]$ ./threading_example202426[lucas@lucas-desktop src]$ ./threading_example172209

但等等,不對啊,程序并沒有數數到30萬,有一次居然只數到10萬,為什么會這樣呢?好吧,加1操作對應實際的處理器指令其實包括:
 

movl  counter(%rip), %eaxaddl  $1, %eaxmovl  %eax, counter(%rip)

首個指令將裝載 counter 的值到 %eax 寄存器,緊接著寄存器的值增1,然后將寄存器的值移給內存中 counter 所在的地址。

我聽到你在嘀咕:這不錯,可為什么會導致數數錯誤的問題呢?嗯,還記得我們以前說過線程會共享處理器,因為只有單核。因此在某些點上,一個線程會依照指令執行完成,但在很多情況下,操作系統會對線程說:時間結束了,到后面排隊再來,然后另外一個線程開始執行,當下一個線程開始執行時,它會從被暫停的那個位置開始執行。所以你猜會發生什么事,當前線程正準備執行寄存器加1操作時,系統把處理器交給另外一個線程?

我真的不知道會發生什么事,可能我們在準備加1時,另外一個線程進來了,重新將 counter 值加載到寄存器等多種情況的產生。誰也不知道到底發生了什么。

正確的做法

解決方案就是要求同一個時間內只允許一個線程訪問共享變量。這個可通過 std::mutex 類來解決。當線程進入時,加鎖、執行操作,然后釋放鎖。其他線程想要訪問這個共享資源必須等待鎖釋放。

互斥(mutex) 是操作系統確保鎖和解鎖操作是不可分割的。這意味著線程在對互斥量進行鎖和解鎖的操作是不會被中斷的。當線程對互斥量進行鎖或者解鎖時,該操作會在操作系統切換線程前完成。

而最好的事情是,當你試圖對互斥量進行加鎖操作時,其他的線程已經鎖住了該互斥量,那你就必須等待直到其釋放。操作系統會跟蹤哪個線程正在等待哪個互斥量,被堵塞的線程會進入 "blocked onm" 狀態,意味著操作系統不會給這個堵塞的線程任何處理器時間,直到互斥量解鎖,因此也不會浪費 CPU 的循環。如果有多個線程處于等待狀態,哪個線程最先獲得資源取決于操作系統本身,一般像 Windows 和 Linux 系統使用的是 FIFO 策略,在實時操作系統中則是基于優先級的。

現在讓我們對上面的代碼進行改進:
 

#include <iostream>#include <string>#include <thread>#include <vector>#include <mutex> using std::thread;using std::vector;using std::cout;using std::endl;using std::mutex; class Incrementer{  private:    int counter;    mutex m;   public:    Incrementer() : counter{0} { };     void operator()()    {      for(int i = 0; i < 100000; i++)      {        this->m.lock();        this->counter++;        this->m.unlock();      }    }     int getCounter() const    {      return this->counter;    } }; int main(){  // Create the threads which will each do some counting  vector<thread> threads;   Incrementer counter;   threads.push_back(thread(std::ref(counter)));  threads.push_back(thread(std::ref(counter)));  threads.push_back(thread(std::ref(counter)));   for(auto &t : threads)  {    t.join();  }   cout << counter.getCounter() << endl;   return 0;}

注意代碼上的變化:我們引入了 mutex 頭文件,增加了一個 m 的成員,類型是 mutex,在operator()() 中我們鎖住互斥量 m 然后對 counter 進行加1操作,然后釋放互斥量。


再次執行上述程序,結果如下:
 

[lucas@lucas-desktop src]$ ./threading_example300000[lucas@lucas-desktop src]$ ./threading_example300000

這下數對了。不過在計算機科學中,沒有免費的午餐,使用互斥量會降低程序的性能,但這總比一個錯誤的程序要強吧。

防范異常

當對變量進行加1操作時,是可能會發生異常的,當然在我們這個例子中發生異常的機會微乎其微,但是在一些復雜系統中是極有可能的。上面的代碼并不是異常安全的,當異常發生時,程序已經結束了,可是互斥量還是處于鎖的狀態。

為了確保互斥量在異常發生的情況下也能被解鎖,我們需要使用如下代碼:
 

for(int i = 0; i < 100000; i++){ this->m.lock(); try  {   this->counter++;   this->m.unlock();  }  catch(...)  {   this->m.unlock();   throw;  }}

但是,這代碼太多了,而只是為了對互斥量進行加鎖和解鎖。沒關系,我知道你很懶,因此推薦個更簡單的單行代碼解決方法,就是使用 std::lock_guard 類。這個類在創建時就鎖定了 mutex 對象,然后在結束時釋放。

繼續修改代碼:
 

void operator()(){  for(int i = 0; i < 100000; i++)  {  lock_guard<mutex> lock(this->m);   // The lock has been created now, and immediatly locks the mutex  this->counter++;   // This is the end of the for-loop scope, and the lock will be  // destroyed, and in the destructor of the lock, it will  // unlock the mutex  }}

上面代碼已然是異常安全了,因為當異常發生時,將會調用 lock 對象的析構函數,然后自動進行互斥量的解鎖。

記住,請使用放下代碼模板來編寫:

 

void long_function(){  // some long code   // Just a pair of curly braces  {  // Temp scope, create lock  lock_guard<mutex> lock(this->m);   // do some stuff   // Close the scope, so the guard will unlock the mutex  }}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩精品在线观看一区二区| 91精品国产自产在线观看永久| 亚州精品天堂中文字幕| 日韩久久免费视频| 欧美日韩国产黄| 亚洲精品乱码久久久久久按摩观| 久久久爽爽爽美女图片| 91久久国产综合久久91精品网站| 欧美在线欧美在线| 亚洲国产日韩欧美在线99| 国产在线视频2019最新视频| 久久久国产91| 国产激情视频一区| 久久精品美女视频网站| 成人免费大片黄在线播放| 亚洲精品视频免费在线观看| 久久久99久久精品女同性| 亚洲变态欧美另类捆绑| 亚洲国产精品小视频| 亚州国产精品久久久| 97成人在线视频| 国产亚洲欧美日韩一区二区| 国产成人免费av| 国产一区二区在线播放| 久久人人爽人人爽人人片av高请| 成人福利视频网| 亚洲性夜色噜噜噜7777| 精品久久久久久久久久ntr影视| 亚洲欧美日韩精品久久奇米色影视| 亚洲精品国产成人| 精品一区二区电影| 国产精品午夜视频| 亚洲视频在线播放| 在线观看不卡av| 国产精品第100页| 国产精品午夜一区二区欲梦| 亚洲精品之草原avav久久| 久久国产精品久久国产精品| 亚洲欧美精品在线| 一级做a爰片久久毛片美女图片| 日韩在线免费av| 日韩在线欧美在线| 欧美日本在线视频中文字字幕| 欧美激情视频在线| 一区二区三区www| 色中色综合影院手机版在线观看| 亚洲成人精品久久久| 另类少妇人与禽zozz0性伦| 亚洲影院色在线观看免费| 国产精品久久一区主播| 最近2019中文字幕在线高清| 国产成人精品视频| 日韩欧美视频一区二区三区| 久久久精品美女| 亚洲国产天堂久久综合| 国产精品极品美女粉嫩高清在线| 亚洲国产精品高清久久久| 精品一区二区亚洲| 国产精品三级久久久久久电影| 日韩成人黄色av| xxxxxxxxx欧美| 中文字幕在线看视频国产欧美在线看完整| 日韩av片电影专区| 久久久国产精彩视频美女艺术照福利| 精品视频在线观看日韩| 久久久国产精品免费| 亚洲iv一区二区三区| 国产在线观看精品一区二区三区| 国产一区二区三区四区福利| 插插插亚洲综合网| 亚洲在线观看视频网站| 91精品国产91| 欧美日韩xxxxx| 国产精品69久久久久| 亚洲影视九九影院在线观看| 久久精品视频网站| 深夜福利亚洲导航| 国产日韩欧美夫妻视频在线观看| 国产剧情久久久久久| 91精品国产99久久久久久| 91在线高清免费观看| 欧美人与性动交| 777午夜精品福利在线观看| 97久久超碰福利国产精品…| xvideos亚洲人网站| 91久久精品日日躁夜夜躁国产| 亚洲人午夜精品免费| 欧美日韩国产精品一区| 国产精品永久免费观看| 亚洲一级免费视频| 国产精品一区二区久久精品| 欧美在线激情视频| 久久久久久久国产精品视频| 久久婷婷国产麻豆91天堂| 国产精品久久久久久久久久久不卡| 国产精品美女在线| 欧美成人精品三级在线观看| 国产精品第一第二| 国产精品极品在线| 国产亚洲精品高潮| 久久久精品久久| 久久色免费在线视频| 美女国内精品自产拍在线播放| 日韩在线激情视频| 性欧美激情精品| 亚洲成人中文字幕| 精品亚洲一区二区三区四区五区| 欧美成人精品一区二区| 久久999免费视频| 亚洲欧美日韩图片| 久久精品国产久精国产一老狼| 中文字幕欧美精品在线| 久久精品一区中文字幕| 国产精品爽爽爽| 久久久久久久香蕉网| 欧美夫妻性生活视频| 亚洲成年网站在线观看| 成人黄色av免费在线观看| 国产suv精品一区二区| 欧美裸体视频网站| 久久免费福利视频| 精品电影在线观看| 久久久999精品| 91免费在线视频网站| 日韩av网站电影| 麻豆国产精品va在线观看不卡| 最新的欧美黄色| 国产精品在线看| 亚洲成人在线视频播放| 久久久久国产视频| 亚洲一区二区三区777| 永久555www成人免费| 欧美成人免费大片| 夜夜嗨av一区二区三区四区| 欧美www视频在线观看| 日本国产一区二区三区| 日韩va亚洲va欧洲va国产| 欧美一区三区三区高中清蜜桃| 国产日韩中文在线| 97精品一区二区视频在线观看| 高清欧美电影在线| 欧美国产日韩在线| 亚洲人成五月天| 日韩av在线导航| 国产精品亚洲一区二区三区| 亚洲性av在线| 亚洲精品视频免费| 国产成人综合精品在线| 97视频在线看| 亚洲xxxx妇黄裸体| 77777亚洲午夜久久多人| 亚洲人成电影网站色xx| 精品无人国产偷自产在线| 欧美精品一区三区| 91精品国产综合久久香蕉922| 日本精品中文字幕| 日韩在线国产精品| 深夜福利一区二区| 欧美高清激情视频| 亚洲欧洲第一视频| 国产欧美一区二区三区四区| 欧美极品在线播放| 激情av一区二区| 91免费福利视频|