c++11多執行緒程式設計(四):資料共享和競爭條件
阿新 • • 發佈:2019-02-13
在多執行緒環境中,執行緒間的資料共享很簡單,但是在程式中這種簡單的資料共享可能會引起問題,其中一種便是競爭條件。
什麼是競爭條件?
競賽條件是發生在多執行緒應用程式中的一種bug
當兩個或多個執行緒並行執行一組操作,訪問相同的記憶體位置,此時,它們中的一個或多個執行緒會修改記憶體位置中的資料,這可能會導致一些意外的結果,這就是競爭條件。
但是,由於所有執行緒同時修改共享資料,在某些情況下,錢包中的錢可能遠小於5000。
測試如下:
這種現象的原因:
每個執行緒並行地增加相同的成員變數“mMoney”,看似是一條線,但是這個“nMoney++”實際上被轉換為3條機器命令:
·在Register中載入"mMoney"變數
·增加register的值
·用register的值更新“mMoney”變數
現在,假設在特定的情況下,上述命令執行程式如下:
什麼是競爭條件?
競賽條件是發生在多執行緒應用程式中的一種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機制,每個執行緒需要在修改或讀取共享資料之前獲取一個鎖,並且在操作完成後,將該鎖釋放。
下一章節繼續討論如何操作