C++多线程:条件变量,notify_one 与notify_all的辨析

作者 : admin 本文共3236个字,预计阅读时间需要9分钟 发布时间: 2024-06-9 共3人阅读

文章目录

  • notify_one
  • notify_all 第一个例子
  • notify_all 第二个例子

notify_one

唤醒等待队列的一个线程:通知条件变量中的一个线程,(若准备好)加入主线程

在下面的程序中,由run给出最初始的notify_one,在线程执行结束时,再给出notify_one通知下一个进程,因此不存在竞争问题,程序会将10个线程全部执行完毕。
若将线程中(即A处)的notify_one()删除,则只有一个线程能执行完毕,其他线程没有得到执行指令会一直阻塞主线程。
值得注意的是,创建线程代码后面需要一定的时间延迟确保线程都被创建出来。

#include
#include
#include
#include
#include
using namespace std;
/*
1.wait()		阻塞线程,直到满足一定条件
2.unique_lock()	加锁线程,防止执行过程中其他线程执行
3.notify_one()	通知条件变量中的一个线程,(若准备好)加入主线程(在全局notify_one的时候无需加锁)
4.notify_all()	通知条件变量中所有的线程,(准备好的)加入主线程(存在竞争现象)
*/

condition_variable cv;	//全局条件变量
mutex mmutex;			//全局互斥锁
bool ready = false;		//全局标志位

void print_id(int id) {
	unique_lock<mutex> lck(mmutex);
	cv.wait(lck, []() {return ready == true; });	//当标志位不满足时,等待
	//上面的语句相当于while(!ready) cv.wait(lck);
	cout << "thread:" << id << endl;
	this_thread::sleep_for(1s);
	
	cv.notify_one();	//A处
}

void run() {
	unique_lock<mutex> lck(mmutex);
	ready = true;
	cv.notify_one();	//B处
}

void test() {
	thread t[10];
	for (int i = 0; i < 10; ++i) {
		t[i] = thread(print_id, i);
	}
	this_thread::sleep_for(0.5s);	//这里延时0.5s确认所有线程都创建完毕。
	cout << "10 threads ready to race..." << endl;
	run();							//对条件进行修改
	for (auto & v : t) {			//线程汇入主线程
		v.join();
	}
}
int main() {
	test();
	return 0;
}

notify_all 第一个例子

会唤醒所有等待队列中阻塞的线程:通知条件变量中所有的线程,(准备好的)加入主线程,因此存在竞争现象。

在下面的代码中,线程之间没有notify_one,但是run中执行了notify_all(),将准备好的线程都加入主线程

#include
#include
#include
#include
#include
using namespace std;
/*
1.wait()		阻塞线程,直到满足一定条件
2.unique_lock()	加锁线程,防止执行过程中其他线程执行
3.notify_one()	通知条件变量中的一个线程,(若准备好)加入主线程(在全局notify_one的时候无需加锁)
4.notify_all()	通知条件变量中所有的线程,(准备好的)加入主线程(存在竞争现象)
*/

condition_variable cv;	//全局条件变量
mutex mmutex;			//全局互斥锁
bool ready = false;		//全局标志位

void print_id(int id) {
	unique_lock<mutex> lck(mmutex);
	cv.wait(lck, []() {return ready == true; });	//当标志位不满足时,等待
	//上面的语句相当于while(!ready) cv.wait(lck);
	cout << "thread:" << id << endl;
	this_thread::sleep_for(1s);
	
	//cv.notify_one();	//A处
}

void run() {
	unique_lock<mutex> lck(mmutex);
	ready = true;
	cv.notify_all();	//B处
}

void test() {
	thread t[10];
	for (int i = 0; i < 10; ++i) {
		t[i] = thread(print_id, i);
	}
	this_thread::sleep_for(0.5s);	//这里延时0.5s确认所有线程都创建完毕。
	cout << "10 threads ready to race..." << endl;
	run();							//对条件进行修改
	for (auto & v : t) {			//线程汇入主线程
		v.join();
	}
}
int main() {
	test();
	return 0;
}

notify_all 第二个例子

或许上面第一个例子没能起到典型的分析作用,来看第二个例子,将唤醒条件设为不同值时,调用两次notify_all()进行唤醒的操作

#include
#include
#include
#include
#include
using namespace std;
condition_variable cv;		//全局条件变量
mutex mmutex;				//全局互斥锁
bool ready[10] = {false};	//全局标志位
void print_id(int id) {
unique_lock<mutex> lck(mmutex);
cout << "thread: " << id <<" waiting" << endl;
cv.wait(lck, [id]() {return ready[id] == true;});	
//当标志位不满足时,阻滞
//上面的语句相当于while(!ready) cv.wait(lck);
cout << "thread:" << id << " finished"<<endl;
this_thread::sleep_for(1s);
}
void signals()
{
this_thread::sleep_for(0.01s);				//等待0.01s确认外部信号变化完毕,锁已经转移到此处
{
std::lock_guard<std::mutex> lk(mmutex);	//在具有锁的情况下更改标志位
cout << "Notifying...
";
for (int i = 1; i < 10; ++i) {
ready[i] = !ready[i - 1];
}
}//将锁释放后可以通知其他线程获取锁
cv.notify_all();				//更改标志位后通知所有线程,让他们相继执行
this_thread::sleep_for(0.01s);	//等待锁转移完成
{	//当其能够执行的线程执行结束的时候mmutex得到解放,下一句得以执行。
std::lock_guard<std::mutex> lk(mmutex);
for (int i = 0; i < 10; ++i) {
ready[i] = !ready[i];
}
cout << "Notifying again...
";
}//将锁释放后可以通知其他线程获取锁
cv.notify_all();
}
void test() {
//create
thread t[10]; thread sig(signals);
for (int i = 0; i < 10; ++i) {
t[i] = thread(print_id, i);
}
cout << "10 threads ready to race..." << endl;
this_thread::sleep_for(0.01s);	//这里延时0.01s确认所有线程都创建完毕
//join only once
for (auto & v : t) {			//所有线程加入主线程
v.join();
}
//notify message
sig.join();						//通知信号加入
}
int main() {
test();
return 0;
}
本站无任何商业行为
个人在线分享 » C++多线程:条件变量,notify_one 与notify_all的辨析
E-->