跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C++实现多线程任务异步调度队列 _ 线程池与共享任务管理设计【源码】

裸用 std::queue 配合多线程易因竞态崩溃,须用 std::unique_lock 保护所有操作;stop() 需原子标志+notify_all() 避免卡死;任务用 std::shared_ptr> 防悬垂;std::jthread 省自动 join 和 stop,但需适配 stop_token;异常必须在线程内 try/catch 捕获。 std::thread + std::queue 容易崩在哪儿 裸用
std::queue
配合多个
std::thread
直接 push/pop,90% 的崩溃来自竞态——
push
和
pop
同时修改队列内部指针,
empty()
和
front()
之间没有原子性。哪怕加了
std::mutex
,漏掉一个
lock_guard
或提前
unlock()
,就会触发未定义行为。 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 所有对队列的访问(
push
、
pop
、
empty
、
size
)必须包裹在同一个
std::unique_lock<:mutex>
下,不能拆成多个锁区间 避免在锁内做耗时操作(比如调用用户回调),否则阻塞整个队列;应只做“取任务”动作,把执行逻辑移出锁区 用
std::condition_variable
替代轮询:消费者线程在
empty()
为真时
wait()
,生产者
notify_one()
即可唤醒,省 CPU 且响应快 为什么别手写线程池的 stop() 逻辑 常见错误是在线程池析构时直接
join()
所有线程,但若某个工作线程正卡在
cv.wait()
,而队列已空、再无新任务、也无人
notify
,它就永远等下去——析构卡死。 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 引入终止标志位
std::atomic stop_requested{false}
,所有工作线程循环中先检查该标志,再决定是否继续等待
stop()
函数需先置
stop_requested = true
,再对每个线程调用
cv.notify_all()
,确保所有等待线程能退出循环 join 前必须确认所有线程已自然退出循环,否则
join()
会阻塞;可加超时检测或用
joinable()
防误调 shared_ptr> 是怎么救场的 任务函数可能捕获局部变量,若用裸函数指针或
std::function
值拷贝,容易发生悬垂引用:任务入队后原作用域结束,lambda 捕获的栈变量已被销毁,执行时读垃圾内存。 C知道 CSDN推出的一款AI技术问答工具 下载 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 任务类型统一定义为
std::shared_ptr<:function>>
,而非
std::function
入队时用
std::make_shared<:function>>([capture...](){...})
,让捕获对象生命周期与任务绑定 注意不要在 lambda 内 capture
this
后又让线程池生命周期短于对象——此时应改用
weak_ptr
配合
lock()
判空,避免循环引用 std::jthread 在 C++20 里到底省了什么 用
std::jthread
可以自动在析构时调用
request_stop()
并
join()
,但前提是你的工作函数接受
std::stop_token
参数,并主动轮询
token.stop_requested()
。否则它和
std::thread
没区别。 实操建议: 立即学习 “ C++免费学习笔记(深入) ”; 工作线程函数签名必须是
void worker(std::stop_token stoken)
,并在循环开头加
if (stoken.stop_requested()) break;
std::jthread
的
get_stop_source().request_stop()
会唤醒
std::condition_variable::wait
并抛出
std::stop_exception
,需 catch 或改用
wait_until
配合
stoken
若项目还停留在 C++17,别强行套
std::jthread
接口,老实用
std::atomic
+
notify_all()
更可控 最常被忽略的是任务执行异常的传播路径:线程池里抛出的异常如果不被捕获,会直接 terminate。得在每条工作线程的顶层加
try/catch(...)
,并提供回调或日志钩子,否则问题只能靠进程崩溃倒推。

相关文章