c++中条件变量condition_variable的使用

本文通过一个C++多线程程序实例,讲解了如何使用`std::condition_variable`进行线程同步。代码展示了在主线程中创建10个子线程,并利用条件变量控制它们的执行顺序。分析了不同环境下,不使用`notify_all()`时程序可能的不同行为,揭示了线程创建速度和唤醒机制的影响。强调了在唤醒线程后需要再次检查条件,以避免不必要的等待。
摘要由CSDN通过智能技术生成

condition_variable是c++中想用条件变量时需要调用的库,最近需要写一个多线程的东西,之前没有接触过,先学习一些基础的知识。
直接上代码(从其他博客拷贝过来的,翻了很多博客都是这个例子):

#include <iostream>                // std::cout
#include <thread>                // std::thread
#include <mutex>                // std::mutex, std::unique_lock
#include <condition_variable>    // std::condition_variable

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


void do_print_id(int id)
{
	std::unique_lock <std::mutex> lck(mtx); // 加锁互斥量
	while (!ready)
	{
		cv.wait(lck); // 当ready==false的时候,while语句执行到wait这里,然后就堵塞到这行,等到通知信号,同时解锁互斥量,不影响其他线程获取锁。 
	}                 //当 cv.notify_all(); // 唤醒所有线程. 执行到这句wait就收到了信号就被唤醒开始干活,首先就是不断的尝试重新获取并加锁互斥量。
					  //若获取不到锁就卡在这里反复尝试加锁
					  //若获取到了锁才往下执行

	std::cout << "thread " << id << '\n';
}

void go()
{
	std::unique_lock <std::mutex> lck(mtx);
	ready = true; // 设置全局标志位为 true.
	cv.notify_all(); // 唤醒所有线程.
}

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

这个代码的意思是在主线程开始时定义10个子线程,先卡住这10个子线程让他们不动,然后再唤醒10个线程,让这10个线程去竞争开跑。上述运行结果是:

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

各个线程的顺序可以不同,但是一定是逐个运行。在函数中有对锁加互斥量

std::unique_lock <std::mutex> lck(mtx)

导致某个线程跑的时候把这个互斥量锁住,其他线程无法获该互斥量。
但是我把进程唤醒注释掉之后,发现代码仍然可以运行,嗯?不是说线程睡着之后需要用notify_all()或者notify_one()唤醒吗?注释掉代码中的cv.notify_all()之后也能跑出上述结果,就比较奇怪。
今天用自己的笔记本跑了一下,注释掉之后代码就会卡在等待唤醒:

10 threads ready to race...

不同设备,同一个代码,跑的效果不同?
do_print_id函数中加一句提示等待的代码后,注释掉唤醒语句,问题真相大白了好像:

void do_print_id(int id)
{
	std::unique_lock <std::mutex> lck(mtx); // 加锁互斥量
	while (!ready)
	{
		std::cout << "wait " << id << '\n';
		cv.wait(lck); 
	}                 
	std::cout << "thread " << id << '\n';
}

这是我的笔记本运行结果:

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

这是公司电脑运行结果:

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

原来是不同电脑创建线程的速度不同。
我的笔记本创建线程速度比较于主线程较快,所以会在主函数走的go()函数之前进入到等待唤醒状态,之后需要唤醒;
而公司电脑则是主函数先走的go()函数,把互斥量直接锁住,导致子线程阻塞住,没有进入等待状态,导致唤醒自然没有作用了,另外ready直接定义成true,直接就跑完所有进程了。
顺便记一下,唤醒子线程之后还需要再次判断一下条件是否满足非等待状态的条件,否则还是会进入等待状态继续等着,直到条件满足唤醒条件。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值