在 C++ 中,条件变量(std::condition_variable
)是多线程编程中常用的同步机制,用于线程间的通信。然而,使用条件变量时可能会遇到 信号丢失 和 虚假唤醒 的问题。以下是这些问题的原因以及解决方法。
1. 信号丢失问题
问题描述:
当一个线程在调用 condition_variable::wait()
之前,另一个线程已经调用了 condition_variable::notify_one()
或 notify_all()
,那么等待的线程可能会错过这个信号,导致它一直阻塞。
解决方法:
- 使用谓词(Predicate):
在调用wait()
时,传入一个谓词(通常是一个 lambda 表达式或函数),用于检查条件是否满足。wait()
会在唤醒后自动检查谓词,如果条件不满足,它会继续等待。std::mutex mtx; std::condition_variable cv; bool ready = false; void waitForReady() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, [] { return ready; }); // 使用谓词避免信号丢失 // 继续执行 } void setReady() { std::unique_lock<std::mutex> lock(mtx); ready = true; cv.notify_one(); // 发送信号 }
2. 虚假唤醒问题
问题描述:
即使没有线程调用 notify_one()
或 notify_all()
,等待的线程也可能被唤醒。这种现象称为 虚假唤醒(Spurious Wakeup)。虚假唤醒是操作系统或底层实现的行为,无法完全避免。
解决方法:
- 使用循环检查条件:
在wait()
返回后,使用一个循环重新检查条件是否满足。如果条件不满足,继续调用wait()
。 - 使用带谓词的
wait()
:
如前所述,wait()
的带谓词版本已经内置了循环检查机制,因此直接使用带谓词的wait()
是最简洁的解决方案。
3. 完整示例
以下是一个完整的示例,展示了如何避免信号丢失和虚假唤醒:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void workerThread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; }); // 使用谓词避免信号丢失和虚假唤醒
std::cout << "Worker thread is processing data." << std::endl;
}
void mainThread() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟一些工作
{
std::unique_lock<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 通知等待的线程
}
int main() {
std::thread worker(workerThread);
std::thread main(mainThread);
worker.join();
main.join();
return 0;
}
4. 总结
- 信号丢失:通过使用带谓词的
wait()
避免信号丢失。 - 虚假唤醒:通过循环检查条件或使用带谓词的
wait()
避免虚假唤醒。 - 最佳实践:始终使用带谓词的
wait()
,它是解决信号丢失和虚假唤醒问题的最简洁和可靠的方式。
通过正确使用条件变量和互斥锁,可以确保多线程程序的正确性和可靠性。
THE END
暂无评论内容