User Tools

Site Tools


c:c_threads:protect_shared_data_or_shared_resources_with_a_mutex

This is an old revision of the document!


C - C++ Threads - Protect shared data or shared resources with a mutex

In a multithreaded environment, more than one thread is often competing for a resource or shared data.

This often results in undefined behavior for the resource or data , unless the resource or data is protected using some mechanics that only allows ONE thread to act on it at a time.

In this example, std::cout is a shared resource that is shared by 6 threads (t1-t5 + main).

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
 
std::mutex mu;
 
void ShowMessage(std::string msg)
{
  std::cout << "Thread " << std::this_thread::get_id() << " says " << msg << std::endl;
}
 
int main()
{
  std::thread t1(ShowMessage, "Hello from Jupiter");
  std::thread t2(ShowMessage, "Hello from Saturn");
  std::thread t3(ShowMessage, "Hello from Mars");
  ShowMessage("Hello from Main/Earth");
  std::thread t4(ShowMessage, "Hello from Uranus");
  std::thread t5(ShowMessage, "Hello from Neptune");
  t1.join();
  t2.join();
  t3.join();
  t4.join();
  t5.join();
  return 0;
}

NOTE: The output will not be in any specific order.

This is because the five threads get the std::cout resource in a random fashion.


Solution

To make the output more deterministic, the solution is to protect the access to std::cout resource using a std::mutex.

Just change the ShowMessage() to acquire a mutex before using std::cout and release it after it is done.

void ShowMessage(std::string msg)
{
  mu.lock();
  std::cout << "Thread " << std::this_thread::get_id() << " says " << msg << std::endl;
  mu.unlock();
}

Another Example

#include <iostream>
#include <thread>
#include <vector>
#include<mutex>
 
 
// The Wallet Class provides a service to add money into a Wallet.
//
// The same Wallet object is used between different threads, so a Lock is needed in the addMoney() method of the Wallet.
//
// A lock is acquired before incrementing the money witin the Wallet and the lock is released before leaving that function.
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();
  } 
};
 
 
// Five threads will share the same Wallet class object and each thread will add 1000 into the Wallet using the addMoney() member function in parallel.
//
// So, if initially the money in the wallet is 0, then then after completion of all the threads, the money in the Wallet should be 5000.
//
// Adding a mutex lock guarantees that Money in the Wallet will be 5000 at the end.
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;
} 

NOTE: The 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.

c/c_threads/protect_shared_data_or_shared_resources_with_a_mutex.1614964694.txt.gz ยท Last modified: 2021/03/05 17:18 by peter

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki