std::shared_ptr存在引用时意外析构——boost::asio协程场景排查
问题排查:Boost.Asio+C++20协程下Socket智能指针引用异常
背景
自研Socket网络库基于Boost.Asio与C++20协程实现:Server类在接受连接时创建Socket类的std::shared_ptr,并将其存入集合管理。Socket通过异步读写持续处理数据,核心代码如下:
Socket异步读写实现
void Socket::read(const String &delim) { boost::asio::async_read_until(_sock, boost::asio::dynamic_buffer(_recBuf), delim, boost::asio::bind_executor(_readStrand, [this, delim = delim, self = shared_from_this()] (const boost::system::error_code& ec, const std::size_t bytes) { // 读处理逻辑... Socket::handleMsg(<the msg>); Socket::read(delim); })); } void Socket::doWrite(std::shared_ptr<String> str) { asio::async_write(_sock, asio::buffer(*str), asio::bind_executor(_writeStrand, [this, str, self = shared_from_this()] (const boost::system::error_code& ec, const std::size_t bytes) { // 写处理逻辑 // 要么从队列取出下一条消息,要么退出 })); }
消息处理逻辑
bool Socket::handleMsg(const object::String &msg) { try { auto self = shared_from_this(); co_spawn(_asyncHandleMsgStrand, [this, self, m] () -> boost::asio::awaitable<void> { try { co_await handleMsgInt(m); } catch (const std::exception &e) { lh.e(fmt::format("{:s}", e.what())); stop_int(); } }, [this, self] (std::exception_ptr e) { if (e) { _ioc.post([e, self] { std::rethrow_exception(e); }); } }); } catch (const std::exception &e) { lh.e(fmt::format("{:s}", e.what())); stop_int(); return false; } return true; }
问题现象
- 当
handleMsgInt()中co_await挂起后,handleMsg()内的协程lambda析构时,捕获的self(std::shared_ptr<Socket>)被析构。此时在Socket析构函数中发现Server仍持有该Socket的引用,但use_count()返回0。 - Socket类存在虚拟菱形继承,且间接继承自
std::enable_shared_from_this。创建Socket实例后,构造函数内的this地址、shared_ptr.get()地址、shared_from_this().get()地址三者不一致,日志如下:
(2024/05/31 18:19:02.775665476) <aaaf> (trace) Socket: this: 0x7fffe8001000 (2024/05/31 18:19:02.775706976) <aaaf> (trace) Server: shared_ptr before adding to sessions set: 0x7fffe8001150, ref: 1 (2024/05/31 18:19:02.775727176) <aaaf> (trace) Server: shared_ptr after adding to sessions: 0x7fffe8001150, ref: 2 (2024/05/31 18:19:02.775739376) <aaaf> (trace) Server: sessions.begin()->get(): 0x7fffe8001150, ref: 2 (2024/05/31 18:19:02.775747376) <aaaf> (trace) Server: shared from this: 0x7fffe8001218, ref: 3
临时解决方法
移除handleMsg()中对shared_from_this()的捕获后,问题消失,但不清楚具体原因。
可能的关联问题与排查方向
- 虚拟菱形继承导致
enable_shared_from_this失效:std::enable_shared_from_this依赖内部弱引用控制块,菱形继承下若多次间接继承该类,会导致控制块重复初始化,或shared_from_this()返回指针指向子对象偏移地址,引发引用计数混乱。需确保继承链中只有最顶层基类直接继承std::enable_shared_from_this,且用虚拟继承避免重复实例化控制块。 shared_from_this()捕获与生命周期不匹配:handleMsg()中self被同时捕获到协程lambda和co_spawn完成处理函数,菱形继承导致this与self.get()地址不一致,可能使智能指针管理的对象和实际操作对象并非同一逻辑实例,引发引用计数提前释放。- 协程挂起后的引用计数混乱:C++20协程挂起时,捕获的变量会转移到协程帧,若
shared_ptr指向地址与this存在偏移,会导致协程帧中的self与Server集合中的shared_ptr指向不同控制块,出现引用计数不统一的情况。 - 析构时
use_count()检查时机错误:Socket进入析构流程后,部分智能指针已销毁,此时use_count()返回值不准确,需在析构前(如stop_int()中)检查引用计数。
内容的提问来源于stack exchange,提问作者bustus_primus
相关产品推荐
相关产品推荐

