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

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

探究在C++程序并發時保護共享數據的問題

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

 我們先通過一個簡單的代碼來了解該問題。
同步問題

我們使用一個簡單的結構體 Counter,該結構體包含一個值以及一個方法用來改變這個值:
 

struct Counter {  int value;   void increment(){    ++value;  }};

然后啟動多個線程來修改結構體的值:

 

int main(){  Counter counter;   std::vector<std::thread> threads;  for(int i = 0; i < 5; ++i){    threads.push_back(std::thread([&counter](){      for(int i = 0; i < 100; ++i){        counter.increment();      }    }));  }   for(auto& thread : threads){    thread.join();  }   std::cout << counter.value << std::endl;   return 0;}

我們啟動了5個線程來增加計數器的值,每個線程增加了100次,然后在線程結束時打印計數器的值。


但我們運行這個程序的時候,我們是希望它會答應500,但事實不是如此,沒人能確切知道程序將打印什么結果,下面是在我機器上運行后打印的數據,而且每次都不同:
 

442500477400422487

問題的原因在于改變計數器值并不是一個原子操作,需要經過下面三個操作才能完成一次計數器的增加:

  •     首先讀取 value 的值
  •     然后將 value 值加1
  •     將新的值賦值給 value

但你使用單線程來運行這個程序的時候當然沒有任何問題,因此程序是順序執行的,但在多線程環境中就有麻煩了,想象下下面這個執行順序:

  •     Thread 1 : 讀取 value, 得到 0, 加 1, 因此 value = 1
  •     Thread 2 : 讀取 value, 得到 0, 加 1, 因此 value = 1
  •     Thread 1 : 將 1 賦值給 value,然后返回 1
  •     Thread 2 : 將 1 賦值給 value,然后返回 1

這種情況我們稱之為多線程的交錯執行,也就是說多線程可能在同一個時間點執行相同的語句,盡管只有兩個線程,交錯的現象也很明顯。如果你有更多的線程、更多的操作需要執行,那么這個交錯是必然發生的。

有很多方法來解決線程交錯的問題:

  •     信號量 Semaphores
  •     原子引用 Atomic references
  •     Monitors
  •     Condition codes
  •     Compare and swap

在這篇文章中我們將學習如何使用信號量來解決這個問題。信號量也有很多人稱之為互斥量(Mutex),同一個時間只允許一個線程獲取一個互斥對象的鎖,通過 Mutex 的簡單屬性就可以用來解決交錯的問題。

使用 Mutex 讓計數器程序是線程安全的

在 C++11 線程庫中,互斥量包含在 mutex 頭文件中,對應的類是 std::mutex,有兩個重要的方法 mutex:lock() 和 unlock() ,從名字上可得知是用來鎖對象以及釋放鎖對象。一旦某個互斥量被鎖,那么再次調用 lock() 返回堵塞值得該對象被釋放。

為了讓我們剛才的計數器結構體是線程安全的,我們添加一個 set:mutext 成員,并在每個方法中通過 lock()/unlock() 方法來進行保護:
 

struct Counter {  std::mutex mutex;  int value;   Counter() : value(0) {}   void increment(){    mutex.lock();    ++value;    mutex.unlock();  }};

然后我們再次測試這個程序,打印的結果就是 500 了,而且每次都一樣。

異常和鎖

現在讓我們來看另外一種情況,想象我們的的計數器有一個減操作,并在值為0的時候拋出異常:
 

struct Counter {  int value;   Counter() : value(0) {}   void increment(){    ++value;  }   void decrement(){    if(value == 0){      throw "Value cannot be less than 0";    }     --value;  }};

然后我們不需要修改類來訪問這個結構體,我們創建一個封裝器:
 

struct ConcurrentCounter {  std::mutex mutex;  Counter counter;   void increment(){    mutex.lock();    counter.increment();    mutex.unlock();  }   void decrement(){    mutex.lock();    counter.decrement();        mutex.unlock();  }};

大部分時候該封裝器運行挺好,但是使用 decrement 方法的時候就會有異常發生。這是一個大問題,一旦異常發生后,unlock 方法就沒被調用,導致互斥量一直被占用,然后整個程序就一直處于堵塞狀態(死鎖),為了解決這個問題我們需要用 try/catch 結構來處理異常情況:
 

void decrement(){  mutex.lock();  try {    counter.decrement();  } catch (std::string e){    mutex.unlock();    throw e;  }  mutex.unlock();}

這個代碼并不難,但看起來很丑,如果你一個函數有 10 個退出點,你就必須為每個退出點調用一次 unlock 方法,或許你可能在某個地方忘掉了 unlock ,那么各種悲劇即將發生,悲劇發生將直接導致程序死鎖。

接下來我們看如何解決這個問題。

自動鎖管理

當你需要包含整段的代碼(在我們這里是一個方法,也可能是一個循環體或者其他的控制結構),有這么一種好的解決方法可以避免忘記釋放鎖,那就是 std::lock_guard.

這個類是一個簡單的智能鎖管理器,但創建 std::lock_guard 時,會自動調用互斥量對象的 lock() 方法,當 lock_guard 析構時會自動釋放鎖,請看下面代碼:

 

struct ConcurrentSafeCounter {  std::mutex mutex;  Counter counter;   void increment(){    std::lock_guard<std::mutex> guard(mutex);    counter.increment();  }   void decrement(){    std::lock_guard<std::mutex> guar(mutex);    mutex.unlock();  }};

是不是看起來爽多了?

使用 lock_guard ,你不再需要考慮什么時候要釋放鎖,這個工作已經由 std::lock_guard 實例幫你完成。

結論

在這篇文章中我們學習了如何通過信號量/互斥量來保護共享數據。需要記住的是,使用鎖會降低程序性能。在一些高并發的應用環境中有其他更好的解決辦法,不過這不在本文的討論范疇之內。

你可以在 Github 上獲取本文的源碼.

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美香蕉大胸在线视频观看| 久久亚洲精品一区二区| 日韩精品欧美国产精品忘忧草| 亚洲精选中文字幕| 国产精品香蕉在线观看| 亚洲第一精品夜夜躁人人躁| 国产一区二区三区在线播放免费观看| 久久精品99久久香蕉国产色戒| 国产精品一区二区三区在线播放| 日韩av电影在线播放| 欧美猛少妇色xxxxx| 亚洲成人免费网站| 日韩不卡中文字幕| 另类色图亚洲色图| 亚洲国产精品字幕| 尤物yw午夜国产精品视频明星| 91精品国产电影| 欧美俄罗斯乱妇| 亚洲人成网站777色婷婷| 色偷偷偷综合中文字幕;dd| 日韩av在线免播放器| 国产69精品久久久久久| 日韩精品视频三区| 91免费看视频.| 亚洲欧美日韩中文在线| 国产91精品高潮白浆喷水| 精品国产91久久久| 欧美猛男性生活免费| 日韩av在线免播放器| 国外日韩电影在线观看| 亚洲国产天堂网精品网站| 欧洲日韩成人av| 久久久天堂国产精品女人| 欧美最猛性xxxxx(亚洲精品)| 亚洲免费小视频| 2018国产精品视频| 庆余年2免费日韩剧观看大牛| 热久久99这里有精品| 粉嫩老牛aⅴ一区二区三区| 欧洲美女7788成人免费视频| 国产精品扒开腿做爽爽爽男男| 欧美成aaa人片在线观看蜜臀| 亚洲精品成a人在线观看| 欧美激情高清视频| 最新的欧美黄色| 丝袜亚洲欧美日韩综合| 亚洲精品日韩av| 欧美亚洲在线视频| 久久手机免费视频| 中文字幕国产精品久久| 91成人在线视频| 国产午夜精品视频免费不卡69堂| 中文字幕亚洲精品| 播播国产欧美激情| 欧美成年人视频网站欧美| 成人疯狂猛交xxx| 中文字幕av一区二区| 日韩av电影国产| 亚洲综合在线小说| 亚洲一区中文字幕在线观看| 久久国产一区二区三区| 7777免费精品视频| 亚洲天堂av图片| 欧美午夜激情在线| 日日噜噜噜夜夜爽亚洲精品| 成人免费视频xnxx.com| 国产xxx69麻豆国语对白| 国产精品久久久一区| 精品久久久久久国产91| 亚洲天堂成人在线| 亚洲网站在线观看| www.国产一区| 97**国产露脸精品国产| 国产亚洲欧美日韩美女| 亚洲一区二区久久| 欧美激情中文字幕乱码免费| 日韩美女毛茸茸| 另类少妇人与禽zozz0性伦| 91精品中国老女人| 欧美精品国产精品日韩精品| 国产精品成人一区二区三区吃奶| 欧美人交a欧美精品| 国产日韩欧美夫妻视频在线观看| 欧美日韩美女在线观看| 亚洲乱码国产乱码精品精天堂| 一区二区欧美激情| 久久99精品视频一区97| 尤物精品国产第一福利三区| 国产精品亚洲美女av网站| 久久精品最新地址| 中文字幕久久久| 欧美性开放视频| 久久在线精品视频| 日韩av在线最新| 日韩成人在线网站| 欧美黄色片免费观看| 精品国产一区二区三区久久久狼| 欧美高清一级大片| 欧美色播在线播放| 国产成人鲁鲁免费视频a| 97视频在线观看免费高清完整版在线观看| 成人在线观看视频网站| 91精品国产亚洲| 国产精品久久久久久久久久尿| 亚洲男子天堂网| 亚洲福利视频网站| 日本成人黄色片| 日韩国产激情在线| 日韩在线视频线视频免费网站| 日韩电影在线观看中文字幕| 久久久亚洲天堂| 亚洲一区第一页| 精品国产老师黑色丝袜高跟鞋| 亚洲视频在线观看免费| 国产欧美日韩中文字幕| 97人洗澡人人免费公开视频碰碰碰| 精品久久久久久久久久| 黑人巨大精品欧美一区二区免费| 亚洲欧美www| 正在播放亚洲1区| 中文字幕欧美日韩在线| 亚洲国产另类 国产精品国产免费| 亚洲新中文字幕| 精品人伦一区二区三区蜜桃免费| 亚洲色图校园春色| 亚洲天堂男人天堂女人天堂| 免费av在线一区| 成人有码视频在线播放| 色哟哟网站入口亚洲精品| 国产精品一区专区欧美日韩| 中文字幕九色91在线| 日韩av三级在线观看| 亚洲精品乱码久久久久久金桔影视| 日韩成人在线播放| 一本色道久久综合狠狠躁篇怎么玩| 午夜精品三级视频福利| 久久久久久噜噜噜久久久精品| 在线视频欧美日韩精品| 亚洲资源在线看| 欧美日韩在线免费| 亚洲最大福利网| 国内精品久久久久久| 日韩精品视频免费在线观看| 中文字幕日韩欧美| 精品欧美一区二区三区| 国产剧情久久久久久| 亚洲精品影视在线观看| 在线视频日韩精品| 欧美激情videoshd| 欧美日本啪啪无遮挡网站| 国产福利视频一区二区| 亚洲a在线播放| 亚洲电影免费观看高清完整版在线观看| 国产免费一区二区三区香蕉精| 国产精品对白刺激| 久久久久久久亚洲精品| 国产成人av网址| 欧美大人香蕉在线| 日韩成人激情影院| 亚洲系列中文字幕| 国产精品丝袜白浆摸在线| 最近中文字幕mv在线一区二区三区四区| 国产精品日日摸夜夜添夜夜av| 国产精品99一区|