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

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 就在下一行。










