如何在C++11中实现带超时的线程等待?
基于C++11实现带超时的跨平台线程等待(替代Windows事件对象)
问题分析
你在Windows下用事件对象实现的主线程超时等待逻辑,核心依赖事件的粘性信号特性——哪怕主线程延迟等待,只要事件已经被触发,WaitForSingleObject就能立即检测到。但直接用C++11的condition_variable会失败,因为它的通知没有粘性:通知只会唤醒当前处于等待状态的线程,主线程如果晚于通知开始等待,就会错过信号直到超时。而用timed_mutex的方案逻辑错误,会导致主线程误判、工作线程死锁。
C++11标准库解决方案:条件变量+状态标志
要模拟Windows事件的粘性特性,需要给condition_variable搭配一个持久的共享状态变量,用来记录工作线程是否完成。通过互斥量保护状态变量的读写,确保状态更新和通知的原子性,同时利用wait_for的谓词版本自动处理状态检查和等待逻辑。
完整代码
#include <thread> #include <chrono> #include <condition_variable> #include <mutex> #include <cstdio> using namespace std; condition_variable cv_worker_done; mutex mtx_worker_done; bool worker_done = false; // 标记工作线程是否完成的共享状态 void ThreadFunc() { printf("worker: launched; working\n"); this_thread::sleep_for(chrono::milliseconds(1000)); // 模拟业务工作 printf("worker: signal done\n"); // 锁定互斥量后更新状态并发送通知 lock_guard<mutex> lk(mtx_worker_done); worker_done = true; cv_worker_done.notify_all(); printf("worker: exit\n"); } int main() { printf("main: launch worker\n"); thread worker(ThreadFunc); printf("main: delay a bit\n"); this_thread::sleep_for(chrono::milliseconds(2000)); // 模拟主线程延迟等待 printf("main: wait for worker's done signal\n"); unique_lock<mutex> lk(mtx_worker_done); // 带谓词的wait_for:先检查状态,满足则直接返回;否则等待,超时或被唤醒后重新检查 bool success = cv_worker_done.wait_for(lk, chrono::milliseconds(5000), []{ return worker_done; }); if (success) { printf("main: worker finished normally\n"); } else { printf("main: worker timeout or error\n"); } worker.join(); return 0; }
方案原理
- 状态变量的粘性特性:
worker_done是持久化的布尔值,工作线程完成后会将其设为true,无论主线程什么时候开始等待,都能通过检查这个变量知道工作状态,不会错过“信号”。 - 互斥量的同步作用:用
mutex保护worker_done的读写,避免工作线程更新状态时,主线程同时检查状态的竞态条件,确保状态更新和通知操作的原子性。 - 带谓词的wait_for:这个重载版本会自动执行以下逻辑:
- 先检查谓词(
worker_done是否为true),如果满足则立即返回true。 - 如果不满足,就进入等待状态,直到超时或被通知唤醒。
- 被唤醒后会再次检查谓词,避免虚假唤醒(标准条件变量的常见问题)。
- 先检查谓词(
预期输出
main: launch worker main: delay a bit worker: launched; working worker: signal done worker: exit main: wait for worker's done signal main: worker finished normally
内容的提问来源于stack exchange,提问作者Chris Korda
相关产品推荐
相关产品推荐

