为什么在使用boost :: asio时需要连接每个连接?

我正在审查Boost网站上的HTTP Server 3示例。

请你们解释为什么我需要每个连接的strand ? 正如我所看到的,我们只在read事件的处理程序中调用read_some 。 所以基本上read_some调用是顺序的,因此不需要链(和第3段的第2项说相同的事情)。 multithreading环境中的风险在哪里?

该文件是正确的。 使用半双工协议实现(如HTTP Server 3) , strand并不是必需的。 呼叫链可以如下所示:

 void connection::start() { socket.async_receive_from(..., &handle_read); ----. } | .------------------------------------------------' | .-----------------------------------------. VV | 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(...) 

如图所示,每个path只启动一个asynchronous事件。 由于不可能同时执行socket_上的处理程序或操作,因此它被认为是以隐含的方式运行。


线程安全

虽然在这个例子中它并不是一个问题,但我想强调一些重要的细节,比如boost::asio::async_write 。 在解释细节之前,先让我们用Boost.Asio来介绍线程安全模型。 对于大多数Boost.Asio对象,在一个对象上挂起多个asynchronous操作是安全的; 只是指定对象的并发调用是不安全的。 在下面的图表中,每一列表示一个线程,每一行代表一个线程正在做的事情。

单线程进行顺序调用是安全的,而其他线程不做任何调用:

  thread_1 |  thread_2
 -------------------------------------- + ----------- ----------------------------
 socket.async_receive(...);  |  ...
 socket.async_write_some(...);  |  ... 

multithreading进行调用是安全的,但不能同时进行:

  thread_1 |  thread_2
 -------------------------------------- + ----------- ----------------------------
 socket.async_receive(...);  |  ...
 ... |  socket.async_write_some(...); 

但是,multithreading并发调用并不安全1

  thread_1 |  thread_2
 -------------------------------------- + ----------- ----------------------------
 socket.async_receive(...);  |  socket.async_write_some(...);
 ... |  ... 

为了防止同时发生的调用,处理程序通常从链内调用。 这是通过以下任一方式完成

  • strand.wrap包装处理程序。 这将返回一个新的处理程序,将通过该分发。
  • 直接通过链发送或发送 。

组合操作的独特之处在于,在处理程序的链中调用stream的中间调用(如果存在的话),而不是启动组合操作的链。 与其他操作相比,这反映了钢绞线的具体位置。 下面是一些关于链使用的示例代码,它将演示一个通过非组合操作读取的套接字,并同时用一个组合操作写入。

 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(); } 

处理程序types的重要性

另外,在组合操作中,Boost.Asio使用参数依赖查找 (ADL)通过完成处理程序的链来调用中间处理程序。 因此,完成处理程序的types具有适当的asio_handler_invoke()钩子是非常重要的。 如果types删除发生在没有合适的asio_handler_invoke()钩子的types中,例如一个boost::function是由strand.wrap的返回typesstrand.wrap ,那么中间处理程序将在链的外部执行,只有完成处理程序将在链中执行。 看到这个答案的更多细节。

在下面的代码中,所有的中间处理程序和完成处理程序都将在代码中执行:

 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. 修订历史logging对此规则的exception。 如果受OS支持,则同步读取,写入,接受和连接操作是线程安全的。 我在这里包括它的完整性,但build议谨慎使用它。

我相信这是因为组合操作async_write 。 async_write是由多个socket :: async_write_someasynchronous组成的。 链有助于序列化这些操作。 阿西奥(asio)的作者克里斯·科尔霍夫(Chris Kohlhoff)在1:17左右的讲话中简要地谈到了这个问题。