C++11多线程编程 第五章: 使用锁来解决竟态条件

C++11 Multithreading – Part 5: Using mutex to fix Race Conditions

Varun February 22, 2015 C++11 Multithreading – Part 5: Using mutex to fix Race Conditions2018-08-18T15:19:37+00:002 Comments

In this article we will discuss how to use mutex locks to protect shared data in multithreaded environment and avoid race conditions.

 

To fix race conditions in multi-threaded environment we need mutex i.e. each thread needs to lock a mutex before modifying or reading the shared data and after modifying the data each thread should unlock the mutex.

std::mutex

In the C++11 threading library, the mutexes are in the <mutex> header file. The class representing a mutex is the std::mutex class.

There are two important methods of mutex:
1.) lock()
2.) unlock()

We have explained Race condition using a Multithreaded Wallet in previous article i.e.

https://thispointer.com//c11-multithreading-part-4-data-sharing-and-race-conditions/

In this article we will see how to use std::mutex to fix the race condition in that multithreaded wallet.

As, Wallet provides a service to add money in Wallet and same Wallet object is used between different threads, so we need to add Lock in addMoney() method of the Wallet i.e.
Acquire lock before increment the money of Wallet and release lock before leaving that function. Let’s see the code,

Wallet class that internally maintains money and provides a service/function i.e. addMoney().
This member function, first acquires a lock then increments the internal money of wallet object by specified count and then releases the lock.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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

    }

};

Now Let’s create 5 threads and all these threads will share a same object of class Wallet and add 1000 to internal money using it’s addMoney() member function in parallel.

So, if initially money in wallet is 0. Then after completion of all thread’s execution money in Wallet should be 5000.

And this mutex lock guarantees that Money in the Wallet will be 5000 at end.

Let’s test this,

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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;

}

It’s guaranteed that it will not found a single scenario where money in wallet is less than 5000.
Because mutex lock in addMoney makes sure that once one thread finishes the modification of money then only any other thread modifies the money in Wallet.

But what if we forgot to unlock the mutex at the end of function. In such scenario, one thread will exit without releasing the lock and other threads will remain in waiting.
This kind of scenario can happen in case some exception came after locking the mutex. To avoid such scenarios we should use std::lock_guard.

std::lock_guard 

std::lock_guard is a class template, which implements the RAII for mutex.
It wraps the mutex inside it’s object and locks the attached mutex in its constructor. When it’s destructor is called it releases the mutex.

Let’s see the code,

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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.

    }

};

 

ps:

这里要注意防止死锁产生. std::lock_guard 即是为了解决因为异常等的产生而导致的锁没有及时释放.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值