多线程程序用Strand替代Mutex:ASIO TCP请求响应实现疑问
用ASIO Strand替代Mutex实现TCP请求响应式通信
核心思路
给每个TCP Socket配一个专属的asio::strand,所有和这个Socket相关的读写、请求处理操作都通过这个Strand来调度。Strand会自动保证同一Socket的操作串行执行,完全能替代之前用的Mutex,从根源上避免操作交织的问题。
具体实现步骤
- 给每个Socket绑定专属Strand
把Socket和Strand封装到同一个类里,创建Socket实例时同时初始化对应的Strand,让两者绑定在一起。 - 把Socket操作绑定到Strand上下文
所有涉及该Socket的异步操作(比如读写),要么用Strand的wrap方法包装处理函数,要么直接在Strand上下文中发起操作。如果用协程的话,先切换到Strand上下文再执行后续操作就行。 - 协程里的正确用法
不用拿strand.post替换co_await,而是让协程的执行逻辑跑在Strand的上下文中。比如处理GRPC请求时,把请求逻辑提交到Strand,或者在协程内部先通过co_await strand.dispatch()切换到Strand,再执行Socket的读写操作。
代码示例
#include <asio.hpp> #include <memory> #include <coroutine> // 封装带Strand的Socket类 class DeviceSocket { public: DeviceSocket(asio::io_context& io_ctx) : socket_(io_ctx), strand_(asio::make_strand(io_ctx)) {} // 协程版设备信息查询 asio::awaitable<void> query_device_info(const std::string& request) { // 先切换到当前Socket的Strand上下文 co_await asio::dispatch(strand_, asio::use_awaitable); // 发送查询请求 co_await asio::async_write(socket_, asio::buffer(request), asio::use_awaitable); // 读取设备响应 char buffer[1024]; std::size_t recv_len = co_await asio::async_read_some(socket_, asio::buffer(buffer), asio::use_awaitable); // 处理响应(比如返回给GRPC侧) std::string response(buffer, recv_len); // ... 这里写你的响应处理逻辑 } // 暴露Socket用于连接等操作 asio::ip::tcp::socket& socket() { return socket_; } private: asio::ip::tcp::socket socket_; // 专属Strand,保证Socket操作串行化 asio::strand<asio::io_context::executor_type> strand_; };
关键说明
上面的示例里,query_device_info协程先通过co_await asio::dispatch(strand_, asio::use_awaitable)切换到Strand上下文,之后的读写操作都会在这个Strand里串行执行。就算GRPC侧同时给同一个DeviceSocket发多个请求,Strand也会把这些请求排成队逐个处理,完全不需要Mutex来加锁。
对你疑问的直接解答
不需要用strand.post替换co_await。strand.post是用来把任务扔进Strand的队列,而co_await是用来等异步操作完成的。正确的做法是把Socket相关的异步操作绑定到Strand上下文,让协程在Strand里跑,这样所有操作自然就串行化了。你可以用asio::dispatch或者asio::post配合use_awaitable来切换上下文,之后的异步操作就会自动在Strand里调度。
内容的提问来源于stack exchange,提问作者kniddl
相关产品推荐
相关产品推荐

