You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现co_spawn_guard:为co_spawn启动的协程创建带取消的shared_ptr

解决co_spawn_guard实现中的段错误问题

我尝试实现一个名为co_spawn_guard的组件,它接受与asio::co_spawn相同的参数,运行传入的协程并返回shared_ptr。该指针指向协程守卫类,当类被销毁时触发已启动协程的取消信号,但实现时出现了段错误。完整代码如下:

#include <cstdint>
#include <list>
#include <vector>
#include <iostream>
#include <concepts>
#include <type_traits>
#include <memory>
#include <thread>
#include <chrono>
 
#define ASIO_HAS_STD_SYSTEM_ERROR

#include <boost/asio.hpp>
#include <boost/asio/experimental/channel.hpp>
#include <boost/asio/experimental/as_tuple.hpp>

using namespace boost;


class coro_guard;

template<typename Executor, typename Awaitable, typename CompletionToken>
std::shared_ptr<coro_guard> co_spawn_guard(Executor, Awaitable&&, CompletionToken&&);
    
class coro_guard : public std::enable_shared_from_this<coro_guard>
{
public:

    template<typename Executor, typename Awaitable, typename CompletionToken>
    friend std::shared_ptr<coro_guard> co_spawn_guard(Executor, Awaitable&&, CompletionToken&&);

    template<typename... Args>
    [[nodiscard]] static std::shared_ptr<coro_guard> create(Args&&... args) 
    {
        return std::shared_ptr<coro_guard>(new coro_guard(std::forward<Args>(args)...));
    }

    coro_guard(const coro_guard&) =delete;;
    coro_guard(coro_guard&&) = delete;
    coro_guard& operator=(const coro_guard&) = delete;
    coro_guard& operator=(coro_guard&&) = delete;

    ~coro_guard()
    {
        asio::post(ex, [cancellation_signal_ptr = cancellation_signal_ptr_]()
        {
            cancellation_signal_ptr->emit(asio::cancellation_type::terminal);
        });
    }

private:
    asio::any_io_executor ex;
    std::shared_ptr<asio::cancellation_signal> cancellation_signal_ptr_;

    explicit coro_guard(asio::any_io_executor e) : ex{e}, cancellation_signal_ptr_{new asio::cancellation_signal{}}{};
};


template<typename Awaitable>
asio::awaitable<void> internal_coro(Awaitable&& a, std::shared_ptr<asio::cancellation_signal> ptr)
{
    std::cout<<"DEBUG internal_coro enter\n";
    co_await std::move(a);
    std::cout<<"DEBUG internal_coro exit\n";
    co_return;
}

template<typename Executor, typename Awaitable, typename CompletionToken>
std::shared_ptr<coro_guard> co_spawn_guard(Executor ex, Awaitable&& a, CompletionToken&& token)
{
    std::shared_ptr<coro_guard> guard =coro_guard::create(ex);
    asio::co_spawn(ex, internal_coro(std::move(a), guard->cancellation_signal_ptr_), asio::bind_cancellation_slot(guard->cancellation_signal_ptr_->slot(), std::forward<CompletionToken>(token)));
    return guard;
}

asio::awaitable<void> test_coro()
{
    std::cout<<"DEBUG internal_coro enter\n";
    std::cout<<"DEBUG internal_coro exit\n";
    co_return;
}

int main()
{
    asio::io_context io_context;

    auto ptr =co_spawn_guard(io_context.get_executor(), test_coro(), asio::detached);

    io_context.run();
    std::cout<<"dupa\n";
}

解决提示

  • 延长守卫对象生命周期至取消任务完成
    析构函数中通过asio::post异步触发取消信号时,原coro_guard对象可能已销毁,导致关联的执行器或信号对象出现悬空引用。修改析构函数,通过shared_from_this()捕获自身的shared_ptr,确保对象生命周期覆盖到取消操作执行:

    ~coro_guard()
    {
        auto self = shared_from_this();
        asio::post(ex, [self]()
        {
            self->cancellation_signal_ptr_->emit(asio::cancellation_type::terminal);
        });
    }
    
  • 确保协程能响应取消信号
    原测试协程test_coro没有可取消的await操作,即使发出取消信号也无法触发取消逻辑。修改测试协程,加入可取消的等待操作,验证取消机制:

    asio::awaitable<void> test_coro()
    {
        std::cout<<"DEBUG test_coro enter\n";
        // 加入可取消的定时器等待
        co_await asio::steady_timer(co_await asio::this_coro::executor, std::chrono::seconds(1))
            .async_wait(asio::use_awaitable);
        std::cout<<"DEBUG test_coro exit\n";
        co_return;
    }
    
  • 保证IO上下文在取消任务执行时有效
    原代码中io_context.run()结束后直接销毁coro_guard,此时IO上下文已停止,取消任务无法被调度执行。修改main函数,重启IO上下文并执行取消任务:

    int main()
    {
        asio::io_context io_context;
    
        auto ptr =co_spawn_guard(io_context.get_executor(), test_coro(), asio::detached);
    
        io_context.run();
        std::cout<<"dupa\n";
        // 销毁守卫,触发取消信号
        ptr.reset();
        // 重启IO上下文执行取消任务
        io_context.restart();
        io_context.run();
    }
    
  • 优化对象创建方式
    将cancellation_signal的创建改为std::make_shared,避免直接使用new,提升内存管理安全性:

    explicit coro_guard(asio::any_io_executor e) 
        : ex{e}, cancellation_signal_ptr_{std::make_shared<asio::cancellation_signal>()}
    {};
    
  • 移除不必要的继承
    若代码中未使用enable_shared_from_this的其他功能,可移除该继承,减少代码复杂性。

内容的提问来源于stack exchange,提问作者Marcus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:54:53