C++ std::condition_variable wait() wait_for() is different from how to use instances

Keywords: C++

1, std::condition_variable is a conditional variable.

2, wait()

When STD:: condition_ When a wait function of the variable object is called, it uses STD:: unique_ Lock (through std::mutex) to lock the current thread. The current thread will be blocked until another thread is in the same STD:: condition_ The variable object calls the notification function to wake up the current thread.

First, let's look at a simple example example

#include <iostream>                // std::cout
#include <thread>                // std::thread
#include <mutex>                // std::mutex, std::unique_lock
#include <condition_variable>    // std::condition_variable
 
std::mutex mtx;              // Global mutex
std::condition_variable cv;  // Global condition variable
bool ready = false;          // Global flag bit
 
void do_print_id(int id)
{
    std::unique_lock <std::mutex> lck(mtx);
    while (!ready) // If the flag bit is not true, wait
        cv.wait(lck); // The current thread is blocked. When the global flag bit becomes true,
    // The thread is awakened and continues to print the thread id
    std::cout << "thread " << id << '\n';
}
void go()
{
    std::unique_lock <std::mutex> lck(mtx);
    ready = true; // Set the global flag bit to true
    cv.notify_all(); // Wake up all threads
}
 
int main()
{
    std::thread threads[10];    // spawn 10 threads:
    for (int i = 0; i < 10; ++i)
        threads[i] = std::thread(do_print_id, i);
    std::cout << "10 threads ready to race...\n";
    go(); // go!
    for (auto & th:threads)
        th.join();
    return 0;
}

The results are as follows:

10 threads ready to race...
thread 1
thread 0
thread 2
thread 3
thread 4
thread 5
thread 6
thread 7
thread 8
thread 9

std::condition_ Introduction to variable:: wait()

unconditional (1)
void wait (unique_lock<mutex>& lck);
predicate (2)
 
    
  1. template <class Predicate>

  2. void wait (unique_lock<mutex>& lck, Predicate pred); In the second case (that is, predict is set), the current thread will be blocked only when the pred condition is false, and the blocking will be unblocked only when PRED is true after receiving the notification from other threads. Therefore, the second case is similar to the following code:

After the current thread calls wait(), it will be blocked (at this time, the current thread should have obtained the lock (mutex), and you may wish to set the lock lck) until another thread calls notify_* Wakes up the current thread.

This function is called automatically when the thread is blocked   lck.unlock() releases the lock so that other threads blocked on lock contention can continue to execute. In addition, once the current thread is notified (usually another thread calls notify_ * to wake up the current thread), the wait() function also automatically calls lck.lock(), making the lck state the same as when the wait function is called.

In the second case (that is, predict is set), the current thread will be blocked only when the pred condition is false, and the blocking will be unblocked only when PRED is true after receiving the notification from other threads.

Therefore, the second case is similar to the following code:

while (!pred()) wait(lck);

Look at the following example( reference resources):

#include <iostream>                // std::cout
#include <thread>                // std::thread, std::this_thread::yield
#include <mutex>                // std::mutex, std::unique_lock
#include <condition_variable>    // std::condition_variable
 
std::mutex mtx;
std::condition_variable cv;
 
int cargo = 0;
bool shipment_available()
{
    return cargo != 0;
}
 
// Consumer thread
void consume(int n)
{
    for (int i = 0; i < n; ++i) {
        std::unique_lock <std::mutex> lck(mtx);
        cv.wait(lck, shipment_available);
        std::cout << cargo << '\n';
        cargo = 0;
    }
}
int main()
{
    std::thread consumer_thread(consume, 10); // Consumer thread
    // The main thread is the producer thread, producing 10 items
    for (int i = 0; i < 10; ++i) {
        while (shipment_available())
            std::this_thread::yield();
        std::unique_lock <std::mutex> lck(mtx);
        cargo = i + 1;
        cv.notify_one();
    }
 
    consumer_thread.join();
 
    return 0;
}

The program execution results are as follows:

concurrency ) ./ConditionVariable-wait 
1
2
3
4
5
6
7
8
9
10

3, wait_for()

std::condition_variable::wait_for() introduction

unconditional (1)
 
    
  1. template <class Rep, class Period>

  2. cv_status wait_for (unique_lock<mutex>& lck,

  3. const chrono::duration<Rep,Period>& rel_time);

predicate (2)
 
    
  1. template <class Rep, class Period, class Predicate>

  2. bool wait_for (unique_lock<mutex>& lck,

  3. const chrono::duration<Rep,Period>& rel_time, Predicate pred);

And   std::condition_variable::wait()   Similarly, however, wait_for can specify a time period. The thread will be blocked before the current thread receives the notification or the specified rel_time timeout. Once the timeout or the notification from other threads is received, wait_for returns, and the remaining processing steps are similar to wait().

In addition, the last parameter PRED of the overloaded version of wait_for (predict (2)) indicates the prediction condition of wait_for. Only when the pred condition is false, the current thread will be blocked by calling wait(), and only when PRED is true after receiving the notification from other threads will it be unblocked. Therefore, it is equivalent to the following code:

return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));

Look at the example below( reference resources ), in the following example, the main thread waits for the th thread to enter a value, and then prints the value received by the th thread from the terminal. Before the th thread receives the value, the main thread waits, times out every second, and prints a "."

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <chrono>             // std::chrono::seconds
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status
 
std::condition_variable cv;
int value;
 
void do_read_value()
{
    std::cin >> value;
    cv.notify_one();
}
 
int main ()
{
    std::cout << "Please, enter an integer (I'll be printing dots): \n";
    std::thread th(do_read_value);
 
    std::mutex mtx;
    std::unique_lock<std::mutex> lck(mtx);
    while (cv.wait_for(lck,std::chrono::seconds(1)) == std::cv_status::timeout) {
        std::cout << '.';
        std::cout.flush();
    }
    std::cout << "You entered: " << value << '\n';
    th.join();
    return 0;
}

C++11: concurrency guide V (detailed explanation of std::condition_variable) _zzhongcy column - CSDN blog

Posted by Jurge on Sat, 09 Oct 2021 06:01:16 -0700