boost::asio异步读写需确保io_context生命周期覆盖整个异步流程,避免局部变量导致pending操作被取消;async_read为全量语义,async_read_some为尽力而为;回调中禁用delete this,应使用shared_ptr管理对象生命周期;定时器须用async_wait而非wait。

boost::asio 异步读写必须绑住 io_context 的生命周期
常见错误是把 io_context 声明成局部变量,然后调用 async_read 或 async_write 后函数就返回了——这时 io_context 析构,所有 pending 的异步操作被无声取消,回调 never 被调用。
正确做法:确保 io_context 的生存期覆盖整个异步流程。常见场景下它得是类成员、全局静态对象,或至少比所有 async_* 调用活得久。
- 不要在函数栈上创建
io_context后直接调用async_connect - 多线程使用时,
io_context::run()必须在至少一个线程里持续运行,否则事件循环停摆 - 如果用
io_context::work防止run()立即退出,注意 work 对象析构会“松开”运行状态,导致后续run()返回
async_read 与 async_read_some 的行为差异很关键
async_read 是“全量语义”:它内部循环调用底层 async_read_some,直到填满你给的 buffer 或出错;而 async_read_some 是“尽力而为”,一次只收当前内核缓冲区里有的数据,可能只收几个字节。
如果你在实现协议解析(比如读一个固定长度的 header),用错就会卡住或解析错位。
- 读固定长度结构(如 4 字节包长)→ 用
async_read+boost::asio::buffer(&len, sizeof(len)) - 做流式解包(如 HTTP chunked body)→ 用
async_read_some,自己管理 buffer 和偏移 -
async_read在超时或连接断开时可能抛boost::system::error_code,不是异常,别漏判ec
回调里不能直接 delete this,除非你完全掌控对象所有权
异步操作的 completion handler(比如 lambda 或 bind 绑定的函数)是在某个线程中执行的,这个线程不一定是你发起调用的线程。如果 handler 里写了 delete this,而此时对象还在被其他地方持有(比如 shared_ptr 持有),就会 double-free 或 use-after-free。
典型踩坑场景:自定义 connection 类,用 std::shared_ptr 管理,但 handler 里手动 delete this。
- 统一用
std::shared_ptr管理 connection 生命周期,handler 写成[self = shared_from_this()](const auto& ec, size_t) { ... } - 避免裸指针 +
delete this,asio 不负责帮你检查对象是否还活着 - 如果必须用裸指针,确保 handler 执行前已调用
cancel()并等待所有 pending 操作完成(很难安全做到)
asio::steady_timer 的 wait() 和 async_wait() 别混用
wait() 是同步阻塞调用,会卡死当前线程;async_wait() 才是真正的异步定时器,靠 io_context 驱动。新手常误以为 wait() 是“轻量版”,结果在主线程里一调就卡住整个服务。
尤其在服务器场景下,任何 wait() 都不该出现在处理请求的路径里。
- 需要延后执行某逻辑(如超时关闭连接)→ 用
async_wait+ handler -
async_wait的 handler 也受io_context生命周期约束,timer 对象本身可以是局部变量,但 handler 执行期间 timer 必须有效(通常用shared_from_this()捕获) - 重复定时任务(如心跳):在 handler 里重新调用
async_wait,别用expires_after循环调wait
最麻烦的地方往往不在语法,而在谁在什么时候销毁什么对象——asio 把控制权交给你,也把责任一起交过来了。buffer 生命周期、handler 捕获方式、io_context 运行状态,三者只要一个没对齐,程序就表现得像“随机失效”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











