condition_variable,wait,notify_one,notify_all

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

  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 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;
}

Tags: C++ Multithreading Concurrent Programming

Posted on Fri, 19 Nov 2021 19:09:10 -0500 by xwishmasterx