Lock and condition variables in C++ multithreading tutorial

  • 2020-06-12 10:02:27
  • OfStack

When doing multithreaded programming, we come across two scenarios:

Multi-threaded access to Shared resources, need to use the lock; State synchronization between multiple threads. Many mechanisms are available. Condition variables are one of the most widely used.

Today I will introduce you to the use of locks and condition variables with a simple example.

The code USES C++11

The sample code


#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
std::mutex       g_mutex;   //  The global lock used 
std::condition_variable g_cond;   //  The conditional variables used 
int g_i    = 0;
bool g_running = true;
void ThreadFunc(int n) {       //  Thread execution function 
 for (int i = 0; i < n; ++i) {
  {
   std::lock_guard<std::mutex> lock(g_mutex);   //  Lock up and leave {} Lock release after scope 
   ++g_i;
   std::cout << "plus g_i by func thread " << std::this_thread::get_id() << std::endl;
  }
 }
 std::unique_lock<std::mutex> lock(g_mutex);    //  lock 
 while (g_running) {
  std::cout << "wait for exit" << std::endl;
  g_cond.wait(lock);                // wait After the call, the lock is first released and then the wait state is entered. When another process call notification is activated, it is locked again 
 }
 std::cout << "func thread exit" << std::endl;
}
int main() {
 int     n = 100;
 std::thread t1(ThreadFunc, n);    //  create t1 Thread ( func thread ), t1 Will perform `ThreadFunc` The instructions in 
 for (int i = 0; i < n; ++i) {
  {
   std::lock_guard<std::mutex> lock(g_mutex);
   ++g_i;
   std::cout << "plus g_i by main thread " << std::this_thread::get_id() << std::endl;
  }
 }
 {
  std::lock_guard<std::mutex> lock(g_mutex);
  g_running = false;
  g_cond.notify_one();   //  Notifying other threads 
 }
 t1.join();     //  Waiting for the thread t1 The end of the 
 std::cout << "g_i = " << g_i << std::endl;
}

After the program is run, the key output is as follows:


plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
plus g_i by func thread 139921006847744
wait for exit               // func thread Waiting for the main thread An exit signal from you 
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
plus g_i by main thread 139921025066816
func thread exit
g_i = 200     //  The locking mechanism ensures that g_i The right of 

As can be seen:


std::this_thread::get_id()
g_i

Introduction of locking method

The code related to locking is:


{
 std::lock_guard<std::mutex> lock(g_mutex);
 ......
}

The point is:

First, this is within 1 local scope, and std::lock_guard is constructed with a call to g_mutex- > lock () method; After the local scope code is finished, std:; The destructor of lock_guard is called, and g_mutex- is called from the function > unlock () method.

This is the process of locking and unlocking, why not just call the lock unlock method?

I think this is because if there is a problem with the code in the middle of locking and unlocking that causes the thread function to exit, then the lock cannot be released until another thread handles it badly, causing a deadlock.

Introduction to the use of conditional variables

lock- is called manually before the thread calls ES47en_cond.wait (lock) > lock(), which is implemented by the constructor std::unique_lock; lock- is called when the thread calls ES57en_cond.wait (lock) to wait > unlock() method, so this is also the previous construction of lock using std::unique_lock; g_cond.notify_one (), which notifies one thread, and g_cond.notify_all (), which notifies all threads; The code for the thread to receive notifications is placed in an while loop to prevent the false notifications mentioned in APUE.

conclusion


Related articles: