最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

探究在C++程序并發(fā)時(shí)保護(hù)共享數(shù)據(jù)的問(wèn)題

 更新時(shí)間:2015年07月14日 11:49:55   投稿:goldensun  
這篇文章主要介紹了探究在C++程序并發(fā)時(shí)保護(hù)共享數(shù)據(jù)的問(wèn)題,也有利于大家更好地理解C++多線程的一些機(jī)制,需要的朋友可以參考下

 我們先通過(guò)一個(gè)簡(jiǎn)單的代碼來(lái)了解該問(wèn)題。
同步問(wèn)題

我們使用一個(gè)簡(jiǎn)單的結(jié)構(gòu)體 Counter,該結(jié)構(gòu)體包含一個(gè)值以及一個(gè)方法用來(lái)改變這個(gè)值:
 

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

然后啟動(dòng)多個(gè)線程來(lái)修改結(jié)構(gòu)體的值:

 

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;
}

我們啟動(dòng)了5個(gè)線程來(lái)增加計(jì)數(shù)器的值,每個(gè)線程增加了100次,然后在線程結(jié)束時(shí)打印計(jì)數(shù)器的值。


但我們運(yùn)行這個(gè)程序的時(shí)候,我們是希望它會(huì)答應(yīng)500,但事實(shí)不是如此,沒(méi)人能確切知道程序?qū)⒋蛴∈裁唇Y(jié)果,下面是在我機(jī)器上運(yùn)行后打印的數(shù)據(jù),而且每次都不同:
 

442
500
477
400
422
487

問(wèn)題的原因在于改變計(jì)數(shù)器值并不是一個(gè)原子操作,需要經(jīng)過(guò)下面三個(gè)操作才能完成一次計(jì)數(shù)器的增加:

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

但你使用單線程來(lái)運(yùn)行這個(gè)程序的時(shí)候當(dāng)然沒(méi)有任何問(wèn)題,因此程序是順序執(zhí)行的,但在多線程環(huán)境中就有麻煩了,想象下下面這個(gè)執(zhí)行順序:

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

這種情況我們稱之為多線程的交錯(cuò)執(zhí)行,也就是說(shuō)多線程可能在同一個(gè)時(shí)間點(diǎn)執(zhí)行相同的語(yǔ)句,盡管只有兩個(gè)線程,交錯(cuò)的現(xiàn)象也很明顯。如果你有更多的線程、更多的操作需要執(zhí)行,那么這個(gè)交錯(cuò)是必然發(fā)生的。

有很多方法來(lái)解決線程交錯(cuò)的問(wèn)題:

  •     信號(hào)量 Semaphores
  •     原子引用 Atomic references
  •     Monitors
  •     Condition codes
  •     Compare and swap

在這篇文章中我們將學(xué)習(xí)如何使用信號(hào)量來(lái)解決這個(gè)問(wèn)題。信號(hào)量也有很多人稱之為互斥量(Mutex),同一個(gè)時(shí)間只允許一個(gè)線程獲取一個(gè)互斥對(duì)象的鎖,通過(guò) Mutex 的簡(jiǎn)單屬性就可以用來(lái)解決交錯(cuò)的問(wèn)題。

使用 Mutex 讓計(jì)數(shù)器程序是線程安全的

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

為了讓我們剛才的計(jì)數(shù)器結(jié)構(gòu)體是線程安全的,我們添加一個(gè) set:mutext 成員,并在每個(gè)方法中通過(guò) lock()/unlock() 方法來(lái)進(jìn)行保護(hù):
 

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

然后我們?cè)俅螠y(cè)試這個(gè)程序,打印的結(jié)果就是 500 了,而且每次都一樣。

異常和鎖

現(xiàn)在讓我們來(lái)看另外一種情況,想象我們的的計(jì)數(shù)器有一個(gè)減操作,并在值為0的時(shí)候拋出異常:
 

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

然后我們不需要修改類來(lái)訪問(wèn)這個(gè)結(jié)構(gòu)體,我們創(chuàng)建一個(gè)封裝器:
 

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

大部分時(shí)候該封裝器運(yùn)行挺好,但是使用 decrement 方法的時(shí)候就會(huì)有異常發(fā)生。這是一個(gè)大問(wèn)題,一旦異常發(fā)生后,unlock 方法就沒(méi)被調(diào)用,導(dǎo)致互斥量一直被占用,然后整個(gè)程序就一直處于堵塞狀態(tài)(死鎖),為了解決這個(gè)問(wèn)題我們需要用 try/catch 結(jié)構(gòu)來(lái)處理異常情況:
 

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

這個(gè)代碼并不難,但看起來(lái)很丑,如果你一個(gè)函數(shù)有 10 個(gè)退出點(diǎn),你就必須為每個(gè)退出點(diǎn)調(diào)用一次 unlock 方法,或許你可能在某個(gè)地方忘掉了 unlock ,那么各種悲劇即將發(fā)生,悲劇發(fā)生將直接導(dǎo)致程序死鎖。

接下來(lái)我們看如何解決這個(gè)問(wèn)題。

自動(dòng)鎖管理

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

這個(gè)類是一個(gè)簡(jiǎn)單的智能鎖管理器,但創(chuàng)建 std::lock_guard 時(shí),會(huì)自動(dòng)調(diào)用互斥量對(duì)象的 lock() 方法,當(dāng) lock_guard 析構(gòu)時(shí)會(huì)自動(dòng)釋放鎖,請(qǐng)看下面代碼:

 

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();
  }
};

是不是看起來(lái)爽多了?

使用 lock_guard ,你不再需要考慮什么時(shí)候要釋放鎖,這個(gè)工作已經(jīng)由 std::lock_guard 實(shí)例幫你完成。

結(jié)論

在這篇文章中我們學(xué)習(xí)了如何通過(guò)信號(hào)量/互斥量來(lái)保護(hù)共享數(shù)據(jù)。需要記住的是,使用鎖會(huì)降低程序性能。在一些高并發(fā)的應(yīng)用環(huán)境中有其他更好的解決辦法,不過(guò)這不在本文的討論范疇之內(nèi)。

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

相關(guān)文章

  • C語(yǔ)言如何建立動(dòng)態(tài)鏈表問(wèn)題

    C語(yǔ)言如何建立動(dòng)態(tài)鏈表問(wèn)題

    這篇文章主要介紹了C語(yǔ)言如何建立動(dòng)態(tài)鏈表問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 老生常談c++中的靜態(tài)成員

    老生常談c++中的靜態(tài)成員

    有時(shí)候需要類的一些成員與類本身相關(guān)聯(lián),而不是與類的每個(gè)對(duì)象相關(guān)聯(lián)。比如類的所有對(duì)象都要共享的變量,這個(gè)時(shí)候我們就要用到類的靜態(tài)成員,今天通過(guò)實(shí)例代碼給大家詳細(xì)介紹,需要的朋友參考下吧
    2021-07-07
  • C++中異常處理的基本思想及throw語(yǔ)句拋出異常的使用

    C++中異常處理的基本思想及throw語(yǔ)句拋出異常的使用

    這篇文章主要介紹了C++中異常處理的基本思想及throw類拋出異常的使用,也深入談到了異常被拋出后的棧解旋unwinding過(guò)程,需要的朋友可以參考下
    2016-03-03
  • C語(yǔ)言學(xué)生成績(jī)管理系統(tǒng)小設(shè)計(jì)

    C語(yǔ)言學(xué)生成績(jī)管理系統(tǒng)小設(shè)計(jì)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言學(xué)生成績(jī)管理系統(tǒng)小設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 使用C++實(shí)現(xiàn)工資管理中的隨機(jī)教師信息生成功能

    使用C++實(shí)現(xiàn)工資管理中的隨機(jī)教師信息生成功能

    這篇文章主要介紹了使用C++實(shí)現(xiàn)工資管理中的隨機(jī)教師信息生成功能,想要做一個(gè)教師工資管理系統(tǒng),就必須得準(zhǔn)備好數(shù)據(jù),但是這些數(shù)據(jù)如果用手一行一行地敲,那么工作量是非常大的,因此,我就產(chǎn)生了用C語(yǔ)言實(shí)現(xiàn)直接生成大量的教師基本信息的想法,需要的朋友可以參考下
    2023-05-05
  • C++ const引用、臨時(shí)變量 引用參數(shù)詳解

    C++ const引用、臨時(shí)變量 引用參數(shù)詳解

    下面小編就為大家?guī)?lái)一篇C++ const引用、臨時(shí)變量 引用參數(shù)詳解。小編覺(jué)得挺不錯(cuò)的現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • 如何用C語(yǔ)言去除字符串兩邊的空字符

    如何用C語(yǔ)言去除字符串兩邊的空字符

    本篇文章是對(duì)用C語(yǔ)言去除字符串兩邊空字符的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    這篇文章主要介紹了StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C語(yǔ)言版學(xué)生成績(jī)管理系統(tǒng)

    C語(yǔ)言版學(xué)生成績(jī)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言版學(xué)生成績(jī)管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C++ 讓函數(shù)返回?cái)?shù)組的方法

    C++ 讓函數(shù)返回?cái)?shù)組的方法

    這篇文章主要介紹了C++ 讓函數(shù)返回?cái)?shù)組的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07

最新評(píng)論

永昌县| 汝南县| 怀柔区| 福泉市| 巴彦淖尔市| 湖州市| 三原县| 通江县| 昆明市| 鹤山市| 松阳县| 家居| 富平县| 云安县| 云浮市| 新郑市| 宁都县| 肃北| 甘孜县| 东山县| 福鼎市| 伊宁县| 亚东县| 安徽省| 七台河市| 文安县| 天津市| 桂东县| 呼伦贝尔市| 敦化市| 隆回县| 迁西县| 太和县| 油尖旺区| 都兰县| 肥西县| 凭祥市| 南涧| 奉化市| 阳东县| 梁河县|