C++ / Concurrency
线程池任务队列整理
线程池最容易出问题的地方不是创建线程,而是任务队列、停止条件和锁的边界。
基本模型
主线程接收连接或解析任务,然后把可执行单元放入队列。工作线程阻塞等待条件变量,被唤醒后取任务执行。这个模型简单,但要把“没有任务”和“线程池正在关闭”区分清楚。
条件变量
等待条件应该写成谓词,而不是裸 wait。原因是条件变量可能虚假唤醒,也可能在多个线程竞争后发现队列已经被取空。
cv.wait(lock, [&] {
return stop || !tasks.empty();
});
停止流程
关闭线程池时,先设置 stop 标记,再 notify_all,最后 join 所有 worker。worker 被唤醒后如果队列为空且 stop 为真,就退出循环。这样可以避免线程永远睡在条件变量上。
边界问题
- 任务函数抛异常时要在 worker 内部捕获。
- 队列过长时需要考虑限流或丢弃策略。
- 不要在持锁状态下执行耗时任务。
- 析构函数里不要再接收新任务。
任务队列的边界
线程池的核心不只是“开几个线程”,而是队列的边界。队列为空时 worker 等待,队列非空时取任务,关闭时唤醒所有 worker 并让它们有机会退出。
异常处理
worker 执行任务时最好捕获异常。否则某个任务抛出异常可能直接结束线程,线程池表面还在运行,实际工作线程已经少了。
try {
task();
} catch (const std::exception& e) {
log_error(e.what());
}