catalogue
1, Condition variable std::condition_variable,wait(),notify_one()
std:: condition_variable is actually a class, a class related to conditions. To put it bluntly, it is waiting for a condition to be reached. This class needs to work with mutexes. When we use it, we need to generate the object of this class.
Example code:
Thread A: wait for A condition to be met
Thread B: specifically throwing messages (data) to the message queue
1.1 wait()
wait() is used to wait for something
- If the second parameter is true, wait() returns directly
- If the return value of the second parameter lambda expression is false, wait () will unlock the mutex and block the line; When will the jam stop? Blocking another thread to call notify_one() member function;
- If wait() does not have a second parameter, my_cond.wait(sbguard), then the effect is the same as the second parameter lambda expression returns false
1.2 notify_ Workflow of one() + wait()
1. When other threads use notify_ After one() wakes up the state of this wait (originally asleep / blocked), the wait starts to work again. What does the recovered wait do?
a) The wait keeps trying to re acquire the mutex lock. If the mutex lock cannot be acquired, the process is stuck in the wait for acquisition. If the mutex lock is acquired, the wait continues to execute b
b) Lock (in fact, if you get the lock, you are locked)
b.1) if wait has the second parameter (lambda), judge the lambda expression. If the lambda expression is false, wait unlocks the mutex, then sleeps, and then waits to be notified_ Wake up
b.2) if lambda is true, wait returns and the process goes down
b.3) if wait() does not have the second parameter, wait() returns and the process goes down
2. To prevent false wake-up: there should be a second parameter (lambda) in wait(), and whether public data exists should be handled correctly in this lambda
#include <iostream> #include <thread> #include <vector> #include <list> #include <string> #include <mutex> using namespace std; class A { public: //A thread that queues the received information (player commands) void inMsgRecvQueue() { for (int i = 0; i < 100000; ++i) { cout << "inMsgRecvQueue()Execute, insert an element" << endl; std::lock_guard<std::mutex> sbguard(my_mutex); msgRecvQueue.push_back(i); //Suppose the number i is the command i received, and i get it directly into the message queue //If the outMsgRecvQueue() is processing a transaction and needs some time, instead of being stuck in wait() waiting for you to wake up, //So at this time, this notify_ The call to one () may not work; my_cond.notify_one(); //We try to wake up the thread of wait(). After this line is executed, the wait in outMsgRecvQueue() will be awakened //Follow up research on what happened after awakening //.. //Process other codes } return; } //A thread that fetches data from a message queue void outMsgRecvQueue() { int command = 0; while (true) { unique_lock<mutex> sbguard(my_mutex); //wait() is used to wait for something //1. If the second parameter is true, wait() returns directly //2. If the return value of the second parameter lambda expression is false, wait() will unlock the mutex and block the line, //When will the jam stop? Blocking another thread to call notify_one() member function; //3. If wait() does not have the second parameter, my_cond.wait(sbguard), then the effect is the same as the second parameter lambda expression returns false //When other threads use notify_ After one() wakes up the state of this wait (originally asleep / blocked), the wait starts to work again. What does the recovered wait do? //a) The wait keeps trying to re acquire the mutex lock. If the mutex lock cannot be acquired, the process is stuck in the wait for acquisition. If the mutex lock is acquired, the wait continues to execute b //b) Lock (in fact, if you get the lock, you are locked) //b.1) if wait has the second parameter (lambda), judge the lambda expression. If the lambda expression is false, wait unlocks the mutex, then sleeps, and then waits to be notified_ Wake up //b.2) if lambda is true, wait returns and the process goes down //b.3) if wait() does not have the second parameter, wait() returns and the process goes down //To prevent false wake-up: there should be a second parameter (lambda) in wait(), and whether public data exists should be handled correctly in this lambda my_cond.wait(sbguard, [this] { //A lambda expression is a callable object (equivalent to a function) if (!msgRecvQueue.empty()) return true; return false; }); //If the process can come here, the mutex must be locked. At the same time, msgRecvQueue has at least one piece of data command = msgRecvQueue.front(); msgRecvQueue.pop_front(); sbguard.unlock(); //Because unique_lock is flexible, so we can unlock it at any time to avoid locking it for too long cout << "outMsgRecvQueue()Execute and take out an element" << command << endl; } } private: std::list<int> msgRecvQueue; //Container, specially used to represent the commands sent by players to us. This is equivalent to shared memory std::mutex my_mutex;//Create a mutex std::condition_variable my_cond; //Generate a condition variable object }; int main() { //Prepare to use member functions as thread functions to write threads; A mytobj; std::thread myOutMsgobj(&A::outMsgRecvQueue, &mytobj); //The second parameter is a reference, so it is not a copy, but detach cannot be used std::thread myInMsgobj(&A::inMsgRecvQueue, &mytobj); myOutMsgobj.join(); myInMsgobj.join();//It doesn't matter who comes first return 0; }
2, Think deeply about the above conditions
notify_ How does one () and wait() work? Think about the process
3, notify_all()
#include <iostream> #include <thread> #include <vector> #include <list> #include <string> #include <mutex> using namespace std; class A { public: //A thread that queues the received information (player commands) void inMsgRecvQueue() { for (int i = 0; i < 100000; ++i) { cout << "inMsgRecvQueue()Execute, insert an element" << endl; std::lock_guard<std::mutex> sbguard(my_mutex); msgRecvQueue.push_back(i); //Suppose the number i is the command i received, and i get it directly into the message queue //If the outMsgRecvQueue() is processing a transaction and needs some time, instead of being stuck in wait() waiting for you to wake up, //So at this time, this notify_ The call to one () may not work; //my_cond.notify_one(); // We try to wake up the thread of wait(). After this line is executed, the wait in outMsgRecvQueue() will be awakened //Follow up research on what happened after awakening my_cond.notify_all(); //Notify all threads //.. //Process other codes } return; } //A thread that fetches data from a message queue void outMsgRecvQueue() { int command = 0; while (true) { unique_lock<mutex> sbguard(my_mutex); //wait() is used to wait for something //1. If the second parameter is true, wait() returns directly //2. If the return value of the second parameter lambda expression is false, wait() will unlock the mutex and block the line, //When will the jam stop? Blocking another thread to call notify_one() member function; //3. If wait() does not have the second parameter, my_cond.wait(sbguard), then the effect is the same as the second parameter lambda expression returns false () //When other threads use notify_ After one() wakes up the state of this wait (originally asleep / blocked), the wait starts to work again. What does the recovered wait do? //a) The wait keeps trying to re acquire the mutex lock. If the mutex lock cannot be acquired, the process is stuck in the wait for acquisition. If the mutex lock is acquired, the wait continues to execute b //b) Lock (in fact, if you get the lock, you are locked) //b.1) if wait has the second parameter (lambda), judge the lambda expression. If the lambda expression is false, wait unlocks the mutex, then sleeps, and then waits to be notified_ Wake up //b.2) if lambda is true, wait returns and the process goes down //b.3) if wait() does not have the second parameter, wait() returns and the process goes down //To prevent false wake-up: there should be a second parameter (lambda) in wait(), and whether public data exists should be handled correctly in this lambda my_cond.wait(sbguard, [this] { //A lambda expression is a callable object (equivalent to a function) if (!msgRecvQueue.empty()) return true; return false; }); //If the process can come here, the mutex must be locked. At the same time, msgRecvQueue has at least one piece of data command = msgRecvQueue.front(); msgRecvQueue.pop_front(); sbguard.unlock(); //Because unique_lock is flexible, so we can unlock it at any time to avoid locking it for too long } } private: std::list<int> msgRecvQueue; //Container, specially used to represent the commands sent by players to us. This is equivalent to shared memory std::mutex my_mutex;//Create a mutex std::condition_variable my_cond; //Generate a condition variable object }; int main() { //Prepare to use member functions as thread functions to write threads; A mytobj; std::thread myOutMsgobj(&A::outMsgRecvQueue, &mytobj); //The second parameter is a reference, so it is not a copy, but detach cannot be used std::thread myInMsgobj(&A::inMsgRecvQueue, &mytobj); myOutMsgobj.join(); myInMsgobj.join();//It doesn't matter who comes first return 0; }