boost::asio中strand在异步读写操作的线程安全与调用顺序问询
问题背景
手册中提到:
在异步组合操作(如async_read()或async_read_until())中,如果完成处理器通过strand执行,那么所有中间处理器也应通过同一strand执行。这是为了确保调用者与组合操作之间共享对象的线程安全访问(以async_read()为例,共享对象是socket,调用者可通过close()取消操作)。这通过为所有中间处理器设置钩子函数,将调用转发到与最终处理器关联的可自定义钩子来实现。
现有如下场景:strand用于异步读取socket操作,socket读取数据后转发至另一异步写入socket,两个操作处于同一io_service中。请问该写入操作是否线程安全?它是否会隐式在同一strand中调用?还是需要显式在strand中调用async_write?
示例代码:
read_socket.async_read_some(my_buffer, boost::asio::bind_executor(my_strand, [](error_code ec, size_t length) { write_socket.async_write_some(boost::asio::buffer(data, size), handler); }));
上述示例中的async_write_some是否会顺序执行,还是同样需要strand?
问题解答
1. 不会隐式绑定到同一strand
你示例中的async_write_some不会自动关联到my_strand。Asio仅会在单个异步组合操作内部(比如async_read这种封装了多次async_read_some的操作)自动将中间处理器绑定到最终handler的strand;而你的场景是两个独立的异步操作(读socket和写socket),不属于同一组合操作,所以Asio不会自动帮你关联strand。
2. 线程安全判断
- 如果
write_socket仅在这个strand的handler中被操作(没有其他线程、其他strand直接调用它的方法),当前写法是线程安全的——因为read的handler在strand中串行执行,不会有并发访问write_socket的情况。 - 如果
write_socket还被其他线程或strand操作(比如其他地方也发起了对它的异步写/读/关闭),那当前写法存在线程安全风险,必须显式将async_write_some的handler绑定到strand,才能避免并发访问socket的问题。
3. 顺序执行的保证
如果不绑定strand,async_write_some的handler可能被io_service的任意线程执行,和其他针对write_socket的异步操作可能并发执行,无法保证执行顺序。只有显式通过bind_executor将async_write_some的handler绑定到strand,才能确保所有针对write_socket的操作handler串行执行,保证顺序和线程安全。
修正后的示例代码
read_socket.async_read_some(my_buffer, boost::asio::bind_executor(my_strand, [](error_code ec, size_t length) { // 显式将write操作的handler绑定到my_strand,确保串行执行 write_socket.async_write_some(boost::asio::buffer(data, size), boost::asio::bind_executor(my_strand, handler)); }));
内容的提问来源于stack exchange,提问作者getsoubl

