C++如何实现跨线程任务传递?(消息队列+回调)

2次阅读

线程安全队列需用std::mutex+std::condition_variable:push/pop加锁,消费者wait时用Lambda谓词检查非空,生产者notify_one()唤醒;handlers映射表读写须加锁,但调用handler需放锁外;std::async必须显式指定std::launch::async以防延迟执行;回调用lambda捕获this并配合shared_ptr管理生命周期。

C++如何实现跨线程任务传递?(消息队列+回调)

std::queue + std::condition_variable 怎么做线程安全队列?

直接用 std::queue 跨线程 push/pop 会崩溃,因为标准容器不是线程安全的。必须加锁,但光加 std::mutex 不够——消费者线程不能轮询检查队列是否为空,否则 CPU 白耗。

  • std::condition_variable 配合 unique_lock 实现“有数据才唤醒”,避免忙等
  • wait() 的谓词必须是 lambda(如 [&]{ return !q.empty(); }),不能只传 q.empty(),否则可能虚假唤醒后直接 pop 空队列
  • 生产者调用 cond.notify_one() 就够了,除非你明确需要多个消费者同时竞争——这时用 notify_all(),但要注意惊群问题

std::function 回调注册为什么必须加锁?

std::map 存消息类型到处理函数的映射(比如 "LOGIN"HandleLogin),但 map 的 operator[]find() 都是非原子操作。多线程一边注册、一边分发,大概率 crash。

  • 所有对 handlers_ 的读写都得包在 std::lock_guard<:mutex></:mutex>unique_lock
  • 别图省事把锁粒度放大到整个消息循环——只锁 map 操作本身, handler 调用要放锁外,否则阻塞其他消息分发
  • std::function<:string message></:string> 而不用裸函数指针,才能绑定成员函数和捕获上下文

std::async 传 std::launch::async 是必须的吗?

是。不显式指定,std::async 可能走延迟执行策略(std::launch::deferred),导致 future.get()主线程同步运行耗时逻辑,彻底失去异步意义。

  • 数据库查询、文件读写这类 IO 密集型任务,一定要用 std::launch::async
  • future.get() 是阻塞的;如果不想卡住,改用 wait_for(100ms) 加超时判断,再配合状态检查
  • 注意 std::async 返回的 std::future 对象不能拷贝,只能 move,否则编译报错 use of deleted function

回调函数怎么访问类成员而不写成 Static

std::bind 或 lambda 捕获 this,比传函数指针+void* 更安全直观。但要注意对象生命周期:如果回调被扔进后台线程执行,而原对象已析构,就会访问野指针。

立即学习C++免费学习笔记(深入)”;

  • 推荐写法:handlers_["LOGIN"] = [this](const Message& m) { return this->HandleLogin(m); };
  • 避免在 lambda 里直接用裸指针或引用成员,改用 shared_ptr 管理对象生命周期
  • 如果 handler 逻辑重,建议在 lambda 里再套一层 std::async,防止回调本身阻塞消息分发线程

跨线程任务传递真正难的不是语法,而是厘清谁负责生命周期、谁持有锁、谁决定线程归属——这些地方一模糊,core dump 就在下一行。

text=ZqhQzanResources