协程退出与智能指针

协程退出与智能指针

该问题是由于智能指针的引用计数问题引起的。fiber使用的智能指针无法析构导致fiber类无法析构。

问题描述

实现sylar协程模块时,遇到了协程退出的问题。 sylar的协程是有栈协程,且只支持主协程与子协程之间的切换。 当一个子协程任务函数执行完毕,需要退出时,需要yeildToHold(swapOut)将控制权转回主协程。但如果是子协程主动yeild,将导致任务函数无法完全完成,对应的协程类的智能指针计数永远大于等于1。如代码所示:

cpp 复制代码
// 协程 执行函数
void Fiber::MainFunc() {
    Fiber::ptr cur_fiber = GetThisFiber();
    M_SYLAR_ASSERT(cur_fiber);

    // 任务函数执行逻辑
    // ...

    // 协程退出逻辑
    cur_fiber->swapOut();

    // 添加assert防止销毁协程重新进入
    M_SYLAR_ASSERT2(false, "fiber has already been destoried, but still swaped in.");
}

如代码所示,Fiber::MainFunc()中持有的cur_fiber是当前协程的智能指针。由于cur_fiber已经swapOut切出了,导致智能指针(cur_fiber)计数永远加一,类永远无法析构。

问题分析

该问题是由于智能指针的引用计数问题引起的。使用的智能指针无法析构导致fiber类无法析构。需要解决函数内的智能指针析构的问题。

问题解决

既然是智能指针引发的问题,那么不用智能指针不就解决了。如代码所示:

cpp 复制代码
// 协程 执行函数
void Fiber::MainFunc() {
    Fiber::ptr cur_fiber = GetThisFiber();
    M_SYLAR_ASSERT(cur_fiber);

    // 任务函数执行逻辑
    // ...

    // 协程退出逻辑
    auto raw_ptr = cur_fiber.get();     // 获取类的指针
    cur_fiber.reset();                  // 使fiber对象引用计数减一
    raw_ptr->swapOut();

    M_SYLAR_ASSERT2(false, "fiber has already been destoried, but still swaped in.");
}

通过使用raw_ptr裸指针,同时重置cur_fiber使得引用计数减一,即可解决智能指针无法销毁的问题。

新的问题

但是这样不会导致raw_ptr变成野指针吗? 其实不会的。

cpp 复制代码
void Fiber::MainFunc() {
    Fiber::ptr cur_fiber = GetThisFiber();
    M_SYLAR_ASSERT(cur_fiber);

    // 任务函数执行逻辑
    // ...

    // runner_fiber 执行完毕
    // 协程退出逻辑
    auto raw_ptr = cur_fiber.get();     // 获取类的指针
    cur_fiber.reset();                  // 使fiber对象引用计数减一
    raw_ptr->swapOut();

    M_SYLAR_ASSERT2(false, "fiber has already been destoried, but still swaped in.");
}

void runner_fiber () {
    m_sylar::Logger::ptr self_logger = M_SYLAR_GET_LOGGER_ROOT();
    m_sylar::Logger::ptr system_logger = M_SYLAR_LOG_NAME("system");
    M_SYLAR_LOG_INFO(system_logger) << "runner_fiber begin";
    m_sylar::Fiber::YieldToHold();          // 交出执行权
    M_SYLAR_LOG_INFO(system_logger) << "runner_fiber end";
}

void runner_thread() {
    // ...
    m_sylar::Fiber::ptr newFiber (new m_sylar::Fiber(runner_fiber));        // 这里有子fiber的一个智能指针。
    newFiber->swapIn();         // 第一次进入fiber
    M_SYLAR_LOG_INFO(self_logger) << "runner_thread swapIn";
    newFiber->swapIn();         // 第二次进入fiber,此次进入后newFiber应该执行完毕

    // ...
}

runner_fiber 执行完毕时,runner_thread中仍持有一个newFiber,所以cur_fiber.reset(); 并不会导致子协程类的析构。 使用gdb调试也印证了这一点。 如果从一开始就用裸指针也同样可以解决问题。

结论与反思

从此次遇到的问题可以做个总结:对于获取的智能指针使用上述方法,不会导致野指针的产生(除非你把它传参出去了)。

相关推荐
程序员cxuan30 分钟前
GPT-5.6 官宣解禁,明天全量发布!
人工智能·后端·程序员
郑州光合科技余经理33 分钟前
海外外卖平台源码改造实战——多语言核心代码实现
java·开发语言·前端·后端·mysql·架构·php
喜欢打篮球的普通人1 小时前
Trition程序编写:从“Hello CUDA“到“Hello Triton“:向量加法背后的编译黑魔法
开发语言·后端·rust
苏三说技术1 小时前
Spring AI 2.0和Spring AI Alibaba,哪个更好?
后端
用户6757049885021 小时前
还在用字符串返回错误提示?Go语言优雅错误处理的正确姿势!
后端·go
雪隐1 小时前
用Flutter做背单词APP-02我画了设计稿,然后让AI帮我设计了数据库(顺便聊了会天)
前端·人工智能·后端
想你依然心痛1 小时前
AUTOSAR Classic Platform架构解析:BSW、RTE与ASW——分层、配置、代码生成
java·架构·实时互动
江华森1 小时前
【玩转 AtomCode】源码编译与架构解析:深入理解开源 AI 编码代理的内核设计
架构·开源
得物技术2 小时前
得物 OceanBase 落地实践
数据库·分布式·架构
用户938515635072 小时前
从零打造你的第一个智能体(Agent):*手写一个能自主建项目的Mini Cursor (三)
javascript·人工智能·后端