正确事件循环需避免while(true)空转,应使用条件变量等待事件;定时器用最小堆管理;回调用std::function配合shared_ptr/weak_ptr确保生命周期安全;线程需join而非detach以防资源泄漏。

事件循环怎么写才不会卡死主线程
直接用 while(true) 轮询会吃满 CPU,且无法响应外部中断;正确做法是让循环等待事件就绪,而不是空转。Linux 下用 epoll_wait,macOS 用 kqueue,Windows 用 WaitForMultipleObjects——但跨平台简单实现,优先选 std::condition_variable 配合 std::mutex。
- 用
std::unique_lock<:mutex></:mutex>加锁后调用cv.wait_for(lock, std::chrono::milliseconds(10)),超时返回继续检查队列,避免永久阻塞 - 事件入队必须在加锁状态下完成,否则
cv.notify_one()可能唤醒前队列还是空的 - 不要在事件回调里做耗时操作(如文件读写、网络请求),否则整个循环被拖慢;该扔进线程池的就扔
如何安全地往事件队列里塞回调函数
裸函数指针难管理生命周期,std::function<void></void> 是更稳妥的选择,但要注意捕获对象的生存期。常见崩溃原因是 lambda 捕获了栈变量或已析构对象。
- 用
std::shared_ptr管理回调依赖的对象,lambda 中按值捕获shared_ptr(如[self = shared_from_this()](){...}) - 避免直接捕获
this,尤其在对象可能被异步删除时;改用weak_ptr做校验:if (auto p = wp.lock()) { p->handle(); } - 事件队列建议用
std::queue<:function>></:function>,别用std::vector—— 频繁 push/pop 时前者是 O(1),后者 pop_front 是 O(n)
定时器事件怎么和事件循环联动
没有现成 setTimeout,得自己维护一个最小堆(std::priority_queue)存待触发的定时任务,每次循环开头检查堆顶是否到期。
- 堆元素类型推荐
std::tuple<:chrono::steady_clock::time_point int std::function>></:chrono::steady_clock::time_point>,用时间点排序,int是唯一 id 防止重复任务干扰 - 插入新定时器时,若原堆为空,需手动
cv.notify_one(),否则循环可能还在等超时,错过立即触发的机会 - 取消定时器不建议从堆中删除(
priority_queue不支持),改用“惰性删除”:执行前检查是否已标记失效,跳过即可
为什么 std::thread 启动事件循环后程序立刻退出
主线程跑完 main() 就销毁所有资源,包括事件循环线程的栈和 std::thread 对象。必须显式等待或分离,但分离有风险。
- 推荐方案:在
main()结尾调用event_loop_thread.join(),并在合适时机(如收到 SIGINT)设置退出标志位 +cv.notify_all() - 绝对不要对刚
detach()的线程调用joinable(),此时状态已不可预测;detach()仅适用于后台守护型逻辑,且确保所有资源都用智能指针管理 - 注意
std::thread析构时若仍可 join,会直接调用std::terminate()—— 这是 silent crash 的高发点
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











