Boost.Asio async_send偶发卡住的原因及底层机制咨询
问题:Boost.Asio async_send 偶尔卡住无返回
我开发了一个基于Boost.Asio库的简单TCP客户端,负责向端点发送数据,但偶尔会出现async_send卡住、流程无法返回的情况。程序正常运行时一切正常,但重启服务后偶尔会出现发送停滞——控制台输出“Start pushing data”后就没有“Done pushing data”的输出,也没有实际数据发送。目前我已经参考Boost的超时示例用deadline timer做了临时解决方案,想了解该问题的原因及底层运行机制。
相关伪代码如下(假设io_service已正常运行且socket连接成功):
some::lock_free::queue<string> q_; // 例如moody_camel的无锁队列 uint64_t max_q_size = 10000; main(){ string str ="hello"; uint64_t size= 5; while(true){ // 用steady timer做10ms的异步等待 addToQueue(&str,size); } } bool addToQueue(string& data, uint64_t size){ if (max_q_size > (cur_q_size + size)) { cout<< "queue capacity at max"<<endl; return false; } else { q_.enqueue(data); cur_q_size+=size; return true; } } dequeueAndSend(){ string& d_; for(auto popped=q_.pop(d_);;popped=q_.pop(d_)){ if(!popped){ // 用steady timer做10ms的异步等待 } else { sendData(d_); } } } sendData(string& buf){ boost::asio::yield_context yc; boost::system::error_code ec; cout<< "Start pushing data"<<endl; socket_.async_send(boost::asio::buffer(buf), yc[ec]); cout<< "Done pushing data"<<endl; return ec; }
原因及底层机制分析
- TCP半开连接:重启场景下,可能出现客户端认为连接仍有效,但服务端已关闭连接且未发送FIN/RST包的情况。此时调用
async_send,内核会尝试发送数据,但对方接收窗口为0或连接已失效,导致发送操作被无限阻塞,Asio无法收到完成事件,协程一直挂起。 - 队列线程安全问题:
cur_q_size未用原子操作保护,多线程环境下会出现竞态条件——实际队列已满但addToQueue仍允许入队,导致发送线程被大量待处理数据压垮,或出现数据损坏引发发送逻辑异常。 - 内核发送缓冲区耗尽:重启时短时间内发送大量数据,填满内核TCP发送缓冲区,
async_send会等待缓冲区有可用空间。若服务端未及时接收数据,这个等待会持续很久,表现为发送流程卡住。 - 协程调度异常:使用
yield_context时,若io_service的事件循环线程资源不足,或重启时事件循环出现短暂异常,async_send的完成事件无法被及时调度,导致协程挂起后无法唤醒。
优化建议
你用deadline timer做超时处理是合理的临时方案,能避免协程无限挂起。但更根本的解决需要:
- 将
cur_q_size改为std::atomic<uint64_t>,确保队列大小计算的线程安全; - 增加心跳机制,定期检测TCP连接状态,发现半开连接及时重建;
- 限制单批次发送的数据量,避免瞬间填满内核发送缓冲区;
- 确保io_service的事件循环线程数量足够,避免调度阻塞。
内容的提问来源于stack exchange,提问作者NamasriAditya
相关产品推荐
相关产品推荐

