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

 

posted @ 2021-11-08 18:35  冰糖葫芦很乖  阅读(55)  评论(0编辑  收藏  举报