链: 使用没有显式锁定的线程
链(strand)被定义为事件处理程序的严格顺序调用(即没有并发调用)。链的使用允许在多线程程序中执行代码而不需要显式锁定(例如使用mutex)。
stand可以是隐式的或者显式的,如以下替代方法所示:
- 仅从一个线程调用io_context::run()意味着所有事件处理程序都在隐式链中执行,因为io_context保证处理程序只能从run()内部调用。
- 如果存在与连接相关的单条异步操作链(例如,在HTTP等半双工协议实现中),则不可能同时执行处理程序,这是一条隐式链。
- 显式链是一个strand<>或者io_context::strand实例。所有事件处理函数对象都需要使用asio::bind_executor()绑定到该实例或者通过链对象 posted/dispatched。
在组合异步操作情况下,例如async_read()或者async_read_until(),如果一个完成处理程序通过一个链,那么所有中间处理程序都需要通过同一个链。这需要确保在调用者和组合操作之间共享的任何对象的线程访问安全(在async_read()场景指的是socket,调用者可以通过close()取消操作)。
为此,所有的异步操作都通过使用get_associated_executor函数来获取处理程序的关联执行器。例如
asio::associated_executor_t<Handler> a = asio::get_associated_executor(h);
关联的执行器必须满足执行器要求。异步操作将使用它来提交中间和最终处理程序以供执行。
可以通过指定嵌套类型executor_type和成员函数get_executor()来为特定的处理程序定制执行器。
class my_handler
{
public:
// Custom implementation of Executor type requirements.
typedef my_executor executor_type;
// Return a custom executor implementation.
executor_type get_executor() const noexcept
{
return my_executor();
}
void operator()() { ... }
};
在更复杂的情况下,associated_executor模板可以直接偏特化:
struct my_handler
{
void operator()() { ... }
};
namespace asio {
template <class Executor>
struct associated_executor<my_handler, Executor>
{
// Custom implementation of Executor type requirements.
typedef my_executor type;
// Return a custom executor implementation.
static type get(const my_handler&,
const Executor& = Executor()) noexcept
{
return my_executor();
}
};
} // namespace asio
asio::bind_executor()函数是一个辅助器用于将特定的执行器对象例如链绑定到完成处理程序。这个绑定会自动关联执行器,如上所示。例如,要将链绑定到完成处理程序,我们只需编写:
my_socket.async_read_some(my_buffer,
asio::bind_executor(my_strand,
[](error_code ec, size_t length)
{
// ...
}));