boost::asio:如何让特定异步函数不并行执行?
解决方案:保证非线程安全C API的串行执行并保留
co_await用法 你遇到的核心问题是:bind_executor(strand, use_awaitable) 仅保证回调的执行被strand序列化,但read_input的启动过程不受strand约束,且read_input自身异步启动线程执行逻辑,导致多个调用会并行触发非线程安全操作。以下是两种可行的解决方案,均保留co_await的使用方式:
方案一:互斥锁绑定read_input生命周期
直接通过互斥锁确保同一时间只有一个read_input实例在运行,锁的持有周期覆盖read_input从启动到回调完成的全流程:
#include <mutex> // 全局互斥锁,保护read_input的执行 std::mutex read_input_mutex; template <typename CompletionToken> auto async_read_input(const char* prompt, CompletionToken&& token) { return asio::async_initiate<CompletionToken, void(std::error_code, std::string)>( [](auto&& completion_handler, const char* prompt) { auto executor = asio::get_associated_executor(completion_handler); // 获取锁并移动到回调中,确保锁持有到read_input完成 std::unique_lock<std::mutex> lock(read_input_mutex); read_input(prompt, [completion_handler = std::move(completion_handler), executor, lock = std::move(lock)](std::error_code ec, std::string s) mutable { // 将结果回调提交到关联的executor(如你的strand) asio::dispatch(executor, [completion_handler = std::move(completion_handler), ec, s = std::move(s), lock = std::move(lock)]() mutable { std::move(completion_handler)(ec, std::move(s)); // 锁在此lambda结束时自动释放,允许下一个read_input执行 }); }); }, token, prompt); }
调用方式保持不变:
co_await async_read_input("Enter your name", asio::bind_executor(strand, asio::use_awaitable));
这种方式简单直接,无需依赖额外Asio特性,专注于保护read_input的串行执行。
方案二:Strand + 异步串行队列(纯Asio原生机制)
如果你希望完全基于Asio的设计理念实现,可以维护与strand绑定的串行任务队列,确保前一个read_input完成后才启动下一个:
#include <asio/experimental/awaitable_operators.hpp> #include <map> // 为每个strand维护一个串行任务的"尾节点" std::map<asio::strand<asio::io_context::executor_type>*, asio::awaitable<void>> strand_pending_ops; template <typename CompletionToken> auto async_read_input(const char* prompt, CompletionToken&& token) { return asio::async_initiate<CompletionToken, void(std::error_code, std::string)>( [](auto&& completion_handler, const char* prompt) { auto executor = asio::get_associated_executor(completion_handler); auto* strand = static_cast<asio::strand<asio::io_context::executor_type>*>(&executor); // 初始化strand对应的任务队列 if (!strand_pending_ops.contains(strand)) { strand_pending_ops[strand] = []() -> asio::awaitable<void> { co_return; }(); } // 定义当前read_input任务的awaitable auto current_task = [prompt]() -> asio::awaitable<std::pair<std::error_code, std::string>> { co_return co_await asio::async_initiate<asio::use_awaitable_t<>, void(std::error_code, std::string)>( [](auto&& handler, const char* prompt) { read_input(prompt, std::move(handler)); }, asio::use_awaitable, prompt); }(); // 更新strand的任务队列:等待上一个任务完成后执行当前任务 strand_pending_ops[strand] = [&]() mutable -> asio::awaitable<void> { co_await strand_pending_ops[strand]; co_await current_task; }(); // 将当前任务结果传递给completion handler asio::co_spawn(executor, [current_task, completion_handler = std::move(completion_handler)]() mutable -> asio::awaitable<void> { auto [ec, s] = co_await current_task; std::move(completion_handler)(ec, std::move(s)); }, asio::detached); }, token, prompt); }
调用方式同样无需修改,这种方案适合需要将read_input与其他strand任务协同串行执行的场景,完全贴合Asio的异步模型。
内容的提问来源于stack exchange,提问作者neucer
相关产品推荐
相关产品推荐

