1. 程式人生 > 其它 >C++11——多執行緒程式設計5

C++11——多執行緒程式設計5

翻譯來自:https://thispointer.com//c11-multithreading-part-5-using-mutex-to-fix-race-conditions/

在本文中,我們將討論如何使用互斥鎖來保護多執行緒環境中的共享資料並避免競爭條件。

為了修復多執行緒環境中的競爭條件,我們需要互斥鎖,即每個執行緒在修改或讀取共享資料之前需要鎖定一個互斥鎖,並且在修改資料之後每個執行緒應該解鎖互斥鎖。

在 C++11 執行緒庫中,互斥鎖位於 <mutex> 標頭檔案中。表示互斥鎖的類是 std::mutex 類。

互斥鎖有兩個重要的方法:
1.) lock()
2.) unlock()

在本文中,我們將看到如何使用 std::mutex 修復多執行緒錢包中的競爭條件。

由於 Wallet 提供了在 Wallet 中新增錢的服務,並且不同執行緒之間使用相同的 Wallet 物件,因此我們需要在 Wallet 的 addMoney() 方法中新增 Lock,即
在增加 Wallet 的錢之前獲取鎖並在離開之前釋放鎖功能。來看看程式碼吧

內部維護貨幣並提供服務/功能的錢包類,即 addMoney()。
該成員函式,先獲取鎖,然後按指定的計數遞增錢包物件的內部貨幣,然後釋放鎖。

#include<iostream>
#include<thread>
#include
<vector> #include<mutex> class Wallet { int mMoney; std::mutex mutex; public: Wallet() :mMoney(0) {} int getMoney() { return mMoney; } void addMoney(int money) { mutex.lock(); for (int i = 0; i < money; ++i) { mMoney++; } mutex.unlock(); } };
int testMultithreadedWallet() { 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 < threads.size(); i++) { threads.at(i).join(); } return walletObject.getMoney(); } int main() { int val = 0; for (int k = 0; k < 1000; k++) { if ((val = testMultithreadedWallet()) != 5000) { std::cout << "Error at count = " << k << " Money in Wallet = " << val << std::endl; //break; } } return 0; }

保證不會發現錢包裡的錢少於5000的單一場景。
因為addMoney中的互斥鎖保證了一旦一個執行緒完成了錢的修改,只有其他執行緒修改了錢包裡的錢。

但是如果我們忘記在函式結束時解鎖互斥鎖呢在這種情況下,一個執行緒將退出而不釋放鎖,其他執行緒將繼續等待。
這種情況可能發生在鎖定互斥鎖後出現異常的情況下。為了避免這種情況,我們應該使用 std::lock_guard。

class Wallet
{
    int mMoney;
    std::mutex mutex;
public:
    Wallet() :mMoney(0){}
    int getMoney()   {     return mMoney; }
    void addMoney(int money)
    {
        std::lock_guard<std::mutex> lockGuard(mutex);
        // In constructor it locks the mutex
        for(int i = 0; i < money; ++i)
        {
            // If some exception occurs at this
            // poin then destructor of lockGuard
            // will be called due to stack unwinding.            mMoney++;
        }
        // Once function exits, then destructor
        // of lockGuard Object will be called.
        // In destructor it unlocks the mutex.
    }
 };