在 C++ 中,std::condition_variable
的 wait
和 wait_for
方法除了可以传入一个锁(std::unique_lock
),还可以传入一个谓词函数 (函数或可调用对象)。这个谓词的作用是让条件变量在特定的条件满足时才退出等待。
1. 谓词的作用
谓词是一个返回布尔值的函数或可调用对象,它用于判断某个条件是否满足。如果条件不满足,wait
会继续阻塞当前线程;如果条件满足,线程会立即退出等待状态。
为什么需要谓词?
- 避免虚假唤醒(Spurious Wakeups)。虚假唤醒是指线程被唤醒后,实际并没有满足预期的条件。
- 简化代码逻辑。通过传入谓词,避免手动使用循环反复检查条件。
2. 常见用法示例
无谓词的使用
如果不使用谓词,必须手动在循环中检查条件:
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
// 使用循环检查条件
while (!ready) {
cv.wait(lock); // 等待唤醒
}
std::cout << "Worker is proceeding!" << std::endl;
}
void notifier() {
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 t1(worker);
std::thread t2(notifier);
t1.join();
t2.join();
return 0;
}
在这种情况下,cv.wait(lock);
只是等待被唤醒,但必须手动在循环中检查 ready
是否为 true
。否则,线程可能会因为虚假唤醒提前退出。
带谓词的使用
通过传入谓词,可以让 wait
方法自动检查条件,避免手动循环:
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
// 使用谓词,只有条件满足时才退出等待
cv.wait(lock, [] { return ready; });
std::cout << "Worker is proceeding!" << std::endl;
}
void notifier() {
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 t1(worker);
std::thread t2(notifier);
t1.join();
t2.join();
return 0;
}
解释:
cv.wait(lock, [] { return ready; });
表示:只有当ready
为true
时,wait
才会退出等待。- 如果
ready
为false
,wait
会重新进入阻塞状态,直到被唤醒并再次检查条件(谓词)。
3. 谓词的优点
(1) 避免虚假唤醒
虚假唤醒可能会导致线程在条件未满足的情况下提前退出等待状态。如果不使用谓词,必须通过循环反复检查条件。
(2) 简化代码逻辑
使用谓词将条件检查逻辑封装到 wait
方法中,代码更简洁、更清晰。
4. 常见的 wait
方法
(1) wait
cpp
cv.wait(lock); // 等待被唤醒
cv.wait(lock, [] { return ready; }); // 等待被唤醒,同时检查谓词
(2) wait_for
wait_for
用于等待一定的时间,并同时检查谓词:
cpp
if (cv.wait_for(lock, std::chrono::seconds(2), [] { return ready; })) {
std::cout << "Condition met within 2 seconds!" << std::endl;
} else {
std::cout << "Timeout!" << std::endl;
}
(3) wait_until
wait_until
用于等待到某个时间点,并同时检查谓词:
cpp
auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(2);
if (cv.wait_until(lock, timeout, [] { return ready; })) {
std::cout << "Condition met before timeout!" << std::endl;
} else {
std::cout << "Timeout!" << std::endl;
}
5. 总结
- 谓词的作用:自动检查条件是否满足,避免虚假唤醒,简化等待逻辑。
- 常见写法 :
cv.wait(lock, [] { return condition; });
,只有当condition
为true
时,线程才会退出等待。 - 使用场景:在多线程程序中,配合条件变量和锁,用于线程间的同步与通信。