文档是正确的。对于半双工协议实现,例如HTTP Server 3,strand 不是必需的。调用链如下所示:
void connection::start()
{
socket.async_receive_from(..., &handle_read); ----.
} |
.------------------------------------------------'
| .-----------------------------------------.
V V |
void connection::handle_read(...) |
{ |
if (result) |
boost::asio::async_write(..., &handle_write); ---|--.
else if (!result) | |
boost::asio::async_write(..., &handle_write); --|--|
else | |
socket_.async_read_some(..., &handle_read); ----' |
} |
.---------------------------------------------------'
|
V
void handle_write(...)
如图所示,每个路径只启动一个异步事件。在socket_ 上不可能同时执行处理程序或操作,据说它在隐式链中运行。
线程安全
虽然它在示例中并未作为问题出现,但我想强调链和组合操作的一个重要细节,例如boost::asio::async_write。在解释细节之前,让我们先介绍一下 Boost.Asio 的线程安全模型。对于大多数 Boost.Asio 对象,在一个对象上挂起多个异步操作是安全的;它只是指定对对象的并发调用是不安全的。在下图中,每一列代表一个线程,每一行代表一个线程在某一时刻正在做什么。
单个线程进行顺序调用而其他线程不进行是安全的:
thread_1 |线程_2
--------------------------------------------------+------------ ----------------------------
socket.async_receive(...); | ...
socket.async_write_some(...); | ...
多线程调用是安全的,但不是并发的:
thread_1 |线程_2
--------------------------------------------------+------------ ----------------------------
socket.async_receive(...); | ...
... | socket.async_write_some(...);
但是,多个线程同时进行调用并不安全1:
thread_1 |线程_2
--------------------------------------------------+------------ ----------------------------
socket.async_receive(...); | socket.async_write_some(...);
... | ...
股
为了防止并发调用,处理程序通常从链中调用。这由以下任一方式完成:
组合操作的独特之处在于,对 流 的中间调用是在 handler 的链中调用的,如果存在的话,而不是在其中组合的链中调用。启动操作。与其他操作相比,这呈现了指定链的位置的倒置。这是一些专注于 strand 使用的示例代码,它将演示通过非组合操作读取的套接字,并通过组合操作同时写入。
void start()
{
// Start read and write chains. If multiple threads have called run on
// the service, then they may be running concurrently. To protect the
// socket, use the strand.
strand_.post(&read);
strand_.post(&write);
}
// read always needs to be posted through the strand because it invokes a
// non-composed operation on the socket.
void read()
{
// async_receive is initiated from within the strand. The handler does
// not affect the strand in which async_receive is executed.
socket_.async_receive(read_buffer_, &handle_read);
}
// This is not running within a strand, as read did not wrap it.
void handle_read()
{
// Need to post read into the strand, otherwise the async_receive would
// not be safe.
strand_.post(&read);
}
// The entry into the write loop needs to be posted through a strand.
// All intermediate handlers and the next iteration of the asynchronous write
// loop will be running in a strand due to the handler being wrapped.
void write()
{
// async_write will make one or more calls to socket_.async_write_some.
// All intermediate handlers (calls after the first), are executed
// within the handler's context (strand_).
boost::asio::async_write(socket_, write_buffer_,
strand_.wrap(&handle_write));
}
// This will be invoked from within the strand, as it was a wrapped
// handler in write().
void handle_write()
{
// handler_write() is invoked within a strand, so write() does not
// have to dispatched through the strand.
write();
}
处理程序类型的重要性
此外,在组合操作中,Boost.Asio 使用argument dependent lookup (ADL) 通过完成处理程序的链调用中间处理程序。因此,完成处理程序的类型具有适当的asio_handler_invoke() 挂钩非常重要。如果类型擦除发生在没有适当的asio_handler_invoke() 钩子的类型上,例如boost::function 是从strand.wrap 的返回类型构造的,那么中间处理程序将在链外执行,并且仅完成处理程序将在链中执行。更多详情见this答案。
在以下代码中,所有中间处理程序和完成处理程序都将在链中执行:
boost::asio::async_write(stream, buffer, strand.wrap(&handle_write));
在下面的代码中,只有完成处理程序会在链中执行。没有一个中间处理程序将在链中执行:
boost::function<void()> handler(strand.wrap(&handle_write));
boost::asio::async_write(stream, buffer, handler);
1。 revision history 记录了此规则的异常情况。如果操作系统支持,同步读取、写入、接受和连接操作是线程安全的。为了完整起见,我将其包含在此处,但建议谨慎使用。