1. Introduction
C11 provides another synchronization mechanism for waiting, which can block one or more threads until a notification or timeout is received from another thread to wake the currently blocked thread. The condition variable is used in conjunction with the mutex.
Condition_variable, with std::unique_lock<std::mutex> for wait operation.
Condition_variable_any, with the use of any mutex with lock, unlock semantics, more flexible, but less efficient than condition_variable.
The condition variables are used as follows:
A. The thread that owns the conditional variable gets the mutex.
B. Loop through a condition and, if the condition is not met, block the thread until it is satisfied, or down if the condition is met.
C. After a thread satisfies the condition and executes, calls Notify_one or Notify_all to wake one or more threads.
2. Practice
You can use conditional variables to implement a synchronization queue, which is used as a thread-safe data share, often for reads between threads, such as synchronization queues for semi-synchronous semi-asynchronous thread pools.
#include <iostream>#include<chrono>#include<thread>#include<mutex>#include<condition_variable>#include<list>Template<typename t>classsyncqueue{ Public: Syncqueue (intmaxSize): M_maxsize (maxSize) {}voidPut (ConstT &t) {Std::lock_guard<std::mutex>Locker (M_mutex); while(Isfull ()) {Std::cout<<"buffer full, need to wait ..."<<Std::endl; M_notfull.wait (M_mutex); } m_queue.push_back (t); M_notempty.notify_one (); } voidTake (ConstT &t) {Std::lock_guard<std::mutex>Locker (M_mutex); while(IsEmpty ()) {Std::cout<<"buffer empty, need to wait ..."<<Std::endl; M_notempty.wait (M_mutex); } t=M_queue.front (); M_queue.pop_front (t); M_notfull.notify_one (); } BOOLEmpty () {Std::lock_guard<std::mutex>Locker (M_mutex); returnM_queue.empty (); } BOOLFull () {Std::lock_guard<std::mutex>Locker (M_mutex); returnM_queue.size () = =m_maxsize; } size_t Size () {Std::lock_guard<std::mutex>Locker (M_mutex); returnm_queue.size (); }Private: BOOLIsfull ()Const { returnM_queue.size () = =m_maxsize; } BOOLIsEmpty ()Const { returnM_queue.empty (); }Private: Std::list<T> M_queue;//buffersStd::mutex M_mutex;//Mutex AmountStd::condition_variable_any M_notempty;//A condition variable that is not emptyStd::condition_variable_any M_notfull;//No full condition variable intM_maxsize;//maximum synchronization queue capacity};
In this queue, the data can be inserted without full, and if it is full, the M_notfull blocking thread waits, waiting for the consuming thread to take out the data, and then the thread that is blocked before is woken up to continue execution, and if the queue is empty, the data cannot be fetched, and the call m_ Notempty to block the current thread, waiting for the inserted data thread to insert data to emit a non-empty notification, wake up the blocked thread, and read the data down.
The wait method for a condition variable also has an overloaded method that can accept a condition.
Std::lock_guard<std::mutex> Locker (M_mutex); while (Isfull ()) { " buffer full, need to wait ... " << Std::endl; M_notfull.wait (M_mutex);}
This can be written as:
Std::lock_guard<std::mutex> Locker (M_mutex); M_notfull.wait (Locker, [thisreturn ! Isfull ();});
Both are the same, the latter code is more concise, the condition variables first check whether the judgment is satisfied with the condition, if satisfied, regain the mutex, end wait, continue to execute; if the condition is not met, release the mutex, set the thread to the waiting state, and continue waiting.
It is important to note that the wait function releases the mutex, and Lock_guard also owns the mutex, which releases the mutex only after the scope has been scoped, so it is not released at this time, but execution wait is released early, and wait is left after wait has released the lock prematurely , after Notify_one/all wakes up, the mutex is acquired first, which is equal to the previous mutex, so the Lock_guard release lock does not cause a problem when it is out of scope.
In this case, if the use of Unique_lock semantics more accurate, because Unique_lock is not like Lock_guard can only be in the destruction of the time to release the lock, it can release the lock at any time, in wait time let Uniq_lock release lock, the semantics more accurate.
In the example above, you can replace the Condition_variable_any with Unique_lock to replace the lock_guard,condition_variable, which makes the code clearer and more efficient.
3. Timeout wait
In addition to wait, you can use the timeout wait function std::condition_variable::wait_for and std::condition_variable::wait_until.
Similar to std::condition_variable::wait (), but wait_for can specify a time period that will be blocked until the current thread receives a notification or the specified time Rel_time times out. Once it has timed out or received notifications from other threads, Wait_for returns, and the remaining processing steps are similar to wait ().
Similar to std::condition_variable::wait_for, but Wait_until can specify a point in time that the thread will be blocked until the current thread receives a notification or a specified point-in-time abs_time times out. And once the timeout is received or the other thread is notified, WAIT_UNTIL returns, and the remaining processing steps are similar to Wait_for ().
//Condition_variable::wait_for Example#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_statusstd::condition_variable CV;intvalue;voidRead_value () {std::cin>>value; Cv.notify_one ();}intMain () {std::cout<<"Please , enter an integer (I'll be printing dots): \ n"; Std::thread th (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::endl; } std::cout<<"You entered:"<< value <<'\ n'; //wait for th thread to finish executingTh.join (); return 0;}
If you use Wait_until, you only need to change the condition to a point in time:
while (Cv.wait_for (Lck,std::chrono::seconds (1)) = =std::cv_status::timeout) while (Cv.wait_until (Lck, Std::chrono::system_clock::now () +std::chrono::seconds (1)) = = Std::cv_status:: Timeout
C11 thread Management: Condition variables