今天在写线程池的时候遇到一个奇怪的情况,在网上也没找到答案,特此记录。
简要描述:在子线程改变了某个值而通知其他线程执行时,其他线程应该会结束在条件变量处的等待,立马进行相应操作,但是遇到了其他线程一直卡住而没有执行得到相应输出的问题。
后面经过 debug 发现是因为在涉及锁和条件变量的代码在一起时,没有明确锁的作用域,造成该锁作用的变量经过修改后,本来是要通知其他线程使用这个修改后的变量的,但是因为锁作用域不当导致其他线程没有及时收到这个变量改变的通知。
接下来写一个最小验证代码说明情况:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print1() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] {
return ready;
});
std::cout << "1" << std::endl;
}
void print2() {
// 问题所在
std::unique_lock<std::mutex> lock(mtx);
ready = true;
std::cout<< 2 <<std::endl;
cv.notify_all();
// 更多耗时操作
}
int main() {
std::thread t1(print1);
std::thread t2(print2);
t1.join();
t2.join();
return 0;
}
在这里注意 print2() 函数,我加锁修改 ready 后,通知其他线程,这时候 print1() 函数应该会接收到通知从而打印 “1”,但是此时终端只打印 2,没有打印 1。原因在于此时 print2 函数中的锁作用于整个 print2 函数,只有当 print2 函数执行完,条件变量的通知才会到 print1 函数那里,造成了print1 函数严重延迟。
当按如下方式在 print2 函数中的锁周围加上作用域后,print1 函数立马执行打印了 1,也就是说 print2 函数发送的通知马上到了 print1 所在线程。
void print2() {
{
std::unique_lock<std::mutex> lock(mtx);
ready = true;
}
std::cout<< 2 <<std::endl;
cv.notify_all();
// 更多耗时操作
}