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

C++20协程中基于ASIO Strand安全访问共享资源的标准实现方式?

ASIO Strand + C++20协程:安全访问共享资源的标准实现

场景与需求

我们需要保护线程不安全的共享资源(如内存池、非线程安全API),通过ASIO Strand串行化所有资源访问操作以保证线程安全。同时基于C++20协程开发,期望通过co_await挂起协程,切换到指定Strand执行资源操作,完成后自动切回协程的原生执行器继续后续逻辑。

排除的方案

  1. 禁用"dispatch两次"的技巧:该方式语法繁琐且存在潜在竞态,示例代码如下:
auto s1 = bind_executor(strand, asio::deferred);
co_await asio::dispatch(s1);
// 访问共享资源
co_await asio::dispatch(asio::deferred);
  1. 拒绝包装为asio::awaitable:避免因use_awaitable引入不必要的协程帧分配。

当前临时方案

已实现一个简易版本(存在未使用concepts、不转发参数、不支持返回值等缺陷),核心逻辑可满足"切换到Strand执行操作后切回原执行器"的需求。

疑问

该场景下的标准实现方式是什么?作为ASIO新手,相信此类需求已有成熟解决方案,但相关资料较少,想确认是否遗漏了ASIO内置的自由函数,或是必须每次通过co_await绑定到Strand的延迟操作?


标准实现:自定义Awaiter

最贴合需求的标准做法是实现自定义awaiter,直接控制执行器切换逻辑——既避免额外协程帧开销,又简化调用语法。

基础版:手动切换执行器

先实现两个基础awaiter,分别负责"切换到Strand"和"切回原执行器":

#include <asio.hpp>
#include <coroutine>

// 切换到指定Strand的awaiter
template <typename Executor>
struct switch_to_strand_awaiter {
    explicit switch_to_strand_awaiter(const Executor& ex) : target_exec_(ex) {}

    bool await_ready() const noexcept {
        // 当前已在目标Strand上则无需切换
        return target_exec_.running_in_this_thread();
    }

    void await_suspend(std::coroutine_handle<> h) const {
        // 调度到目标Strand后恢复协程
        asio::dispatch(target_exec_, [h]() { h.resume(); });
    }

    void await_resume() const noexcept {}

private:
    Executor target_exec_;
};

// 切回原执行器的awaiter
template <typename Executor>
struct switch_back_awaiter {
    explicit switch_back_awaiter(const Executor& ex) : original_exec_(ex) {}

    bool await_ready() const noexcept {
        return original_exec_.running_in_this_thread();
    }

    void await_suspend(std::coroutine_handle<> h) const {
        asio::dispatch(original_exec_, [h]() { h.resume(); });
    }

    void await_resume() const noexcept {}

private:
    Executor original_exec_;
};

// 辅助函数简化创建
template <typename Executor>
auto switch_to_strand(const Executor& ex) {
    return switch_to_strand_awaiter<Executor>(ex);
}

使用方式

asio::io_context ioctx;
auto strand = asio::make_strand(ioctx);

asio::awaitable<void> example_coroutine() {
    // 保存当前执行器
    auto original_exec = co_await asio::this_coro::executor;
    
    // 切换到Strand执行资源操作
    co_await switch_to_strand(strand);
    shared_resource->unsafe_operation(); // 此时在Strand串行执行
    
    // 切回原执行器继续逻辑
    co_await switch_back_awaiter(original_exec);
    do_other_work();
}

进阶版:封装自动切换的高阶函数

如果希望更简洁的语法,可以封装一个高阶函数,自动处理"切换到Strand执行操作→切回原执行器"的完整流程,同时支持返回值:

template <typename Executor, typename Func>
auto run_on_strand(const Executor& strand, Func&& func) {
    struct awaiter {
        Executor strand_;
        Func func_;
        std::invoke_result_t<Func> result_;

        awaiter(const Executor& s, Func&& f) 
            : strand_(s), func_(std::forward<Func>(f)) {}

        bool await_ready() const noexcept {
            return strand_.running_in_this_thread();
        }

        template <typename Promise>
        void await_suspend(std::coroutine_handle<Promise> h) {
            auto original_exec = h.promise().executor();
            // 调度到Strand执行操作,完成后切回原执行器恢复协程
            asio::dispatch(strand_, [this, h, original_exec]() {
                result_ = std::invoke(std::move(func_));
                asio::dispatch(original_exec, [h]() { h.resume(); });
            });
        }

        decltype(auto) await_resume() {
            return std::forward<decltype(result_)>(result_);
        }
    };
    return awaiter(strand, std::forward<Func>(func));
}

使用方式

asio::awaitable<void> example_coroutine() {
    // 自动切换到Strand执行操作,完成后切回原执行器
    auto resource_result = co_await run_on_strand(strand, []() {
        return shared_resource->compute_value();
    });
    
    // 在原执行器处理结果
    process_result(resource_result);
}

方案优势

  1. 无额外协程帧:自定义awaiter直接操作协程句柄,无需依赖use_awaitable创建嵌套协程。
  2. 清晰的执行流:手动切换版本明确控制执行器切换时机,封装版本则简化重复逻辑。
  3. 符合ASIO设计:完全基于ASIO的执行器调度机制,保证线程安全的同时保留协程的异步语义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 20:50:27