1. 程式人生 > >c++11多執行緒程式設計(四):資料共享和競爭條件

c++11多執行緒程式設計(四):資料共享和競爭條件

在多執行緒環境中,執行緒間的資料共享很簡單,但是在程式中這種簡單的資料共享可能會引起問題,其中一種便是競爭條件。

什麼是競爭條件?
競賽條件是發生在多執行緒應用程式中的一種bug
當兩個或多個執行緒並行執行一組操作,訪問相同的記憶體位置,此時,它們中的一個或多個執行緒會修改記憶體位置中的資料,這可能會導致一些意外的結果,這就是競爭條件。

競爭條件通常較難發現並重現,因為它們並不總是出現,只有當兩個或多個執行緒執行操作的相對順序導致意外結果時,它們才會發生,通過例子理解:

class Wallet {
  int mMoney;
 public:
  Wallet() : mMoney(0) { }
  int getMoney() { return mMoney; }
  void addMoney(int money) {
    for (int i = 0; i < money; i++) {
      mMoney++;
    }
  }
}; 

建立5個執行緒,這些執行緒共享類Wallet的一個物件,使用addMoney()成員函式並行新增1000內部資金。

所以,如果最初錢包中的錢是0,那麼在所有執行緒的競爭執行完畢後,錢包中的錢應該是5000,
但是,由於所有執行緒同時修改共享資料,在某些情況下,錢包中的錢可能遠小於5000。
測試如下:
#include <iostream>
#include <thread>
#include <algorithm>  

class Wallet {  
  int mMoney;  
 public:  
  Wallet() : mMoney(0) { }  
  int getMoney() { return mMoney; }  
  void addMoney(int money) {  
    for (int i = 0; i < money; i++) {  
      mMoney++;  
    }  
  }  
};  

int testMultithreadWallet() {  
  Wallet walletObject;  
  std::vector<std::thread> threads;  
  for (int i = 0; i < 5; i++) {  
    threads.push_back(std::thread(&Wallet::addMoney, &walletObject, 1000));  
  }  
  
  for (int i = 0; i < 5; i++) {  
    threads.at(i).join();  
  }  
  
  return walletObject.getMoney();  
}  
  
int main() {  
  int val = 0;  
  for (int k = 0; k < 1000; k++) {  
    if ((val == testMultithreadWallet()) != 5000) {  
      std::cout << "Error at count = " << k << " Money in Wallet = " << val << std::endl;  
    }  
  }  
  
  return 0;  
}  

由於相同Wallet類物件的成員函式addMoney()執行了5次,所以money預計為50000,但由於addMoney()成員函式並行執行,因此在某些情況下,mMoney可能遠小於5000
輸出:

Error at count = 971  Money in Wallet = 4568
Error at count = 971  Money in Wallet = 4568
Error at count = 972  Money in Wallet = 4260
Error at count = 972  Money in Wallet = 4260
Error at count = 973  Money in Wallet = 4976
Error at count = 973  Money in Wallet = 4976

這種現象的原因:
每個執行緒並行地增加相同的成員變數“mMoney”,看似是一條線,但是這個“nMoney++”實際上被轉換為3條機器命令:
·在Register中載入"mMoney"變數
·增加register的值
·用register的值更新“mMoney”變數

現在,假設在特定的情況下,上述命令執行程式如下:
執行緒1: 執行緒2:
在暫存器中載入“mMoney”變數
在暫存器中載入“mMoney”變數
增加暫存器的值
增加暫存器的值
使用暫存器中的值更新“mMoney”變數
使用暫存器中的值更新“mMoney”變數

在這種情況下,一個增量將被忽略,因為不是增加mMoney變數兩次,而是增加不同的暫存器,“mMoney”變數的值被覆蓋。

假設在這種情況前,mMoney的值是46.如上圖所示,它增加了2次,所以預期結果是48.但是由於上述情況下的競爭條件,mMoney最終的值僅為47
這就叫做競爭條件。

怎樣修復這種競爭條件
為了解決這個問題,我們需要使用lock機制,每個執行緒需要在修改或讀取共享資料之前獲取一個鎖,並且在操作完成後,將該鎖釋放。

下一章節繼續討論如何操作