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
,直接就跑完所有进程了。
顺便记一下,唤醒子线程之后还需要再次判断一下条件是否满足非等待状态的条件,否则还是会进入等待状态继续等着,直到条件满足唤醒条件。