为什么我需要每个连接搁浅使用boost :: ASIO的时候? [英] Why do I need strand per connection when using boost::asio?

查看:99
本文介绍了为什么我需要每个连接搁浅使用boost :: ASIO的时候?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我检讨<一个href=\"http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/examples.html#boost_asio.examples.http_server_3\">HTTP在Boost的网站服务器3 的例子。

难道你们,请解释一下为什么我需要每个连接?我所看到的,我们称之为 read_some 仅在读的事件处理程序。所以基本上 read_some 呼叫顺序,因此没有必要对股(和的item第三段说同样的事情)2。哪里是在多线程环境中的风险?

Could you guys please explain why I need strand per connection ? As I can see we call read_some only in handler of read-event. So basically read_some calls are sequential therefore there is no need for strand (and item 2 of 3rd paragraph says the same thing). Where is the risk in multi-threading environment?

推荐答案

该文档是正确的。与半双工协议实现,如<一个href=\"http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/example/cpp03/http/server3/connection.cpp\">HTTP服务器3 ,在是没有必要的。调用链可以说明如下:

The documentation is correct. With a half duplex protocol implementation, such as HTTP Server 3, the strand is not necessary. The call chains can be illustrated as follows:

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(...)

如该图所示,只有一个单一的异步事件每个路径开始。随着处理或操作的并发执行对插座_ 没有可能,据说在隐含股正在运行。

As shown in the illustration, only a single asynchronous event is started per path. With no possibility of concurrent execution of the handlers or operations on socket_, it is said to be running in an implicit strand.

虽然它没有present自己作为例子的一个问题,我想强调链中的一条重要的细节和组成的操作,如<一个href=\"http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_write.html\"><$c$c>boost::asio::async_write.在解释的细节,让我们先覆盖Boost.Asio的线程安全模式。对于大多数Boost.Asio的目的,它是安全的具有在物体上待处理多个异步操作;它只是指定的对象上并发调用是不安全的。下图中,再$ P $每列psents一个线程,每条线再presents什么线程在片刻时间做。

While it does not present itself as an issue in the example, I would like to highlight one important detail of strands and composed operations, such as boost::asio::async_write. Before explaining the details, lets first cover the thread safety model with Boost.Asio. For most Boost.Asio objects, it is safe to have multiple asynchronous operations pending on an object; it is just specified that concurrent calls on the object are unsafe. In the diagrams below, each column represents a thread and each line represents what a thread is doing at a moment in time.

这是安全的一个单独的线程进行顺序调用,而其他线程不会作:

It is safe for a single thread to make sequential calls while other threads make none:

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

这是安全的多线程来拨打电话,但不能同时使用:

It is safe for multiple threads to make calls, but not concurrently:

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

然而,这是不安全的多个线程来拨打电话的同时 1

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

绞线

要prevent并发调用,处理程序通常是由股中调用。这是通过完成:

Strands

To prevent concurrent invocations, handlers are often invoked from within strands. This is done by either:


  • 包装纸与处理程序<一个href=\"http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/io_service__strand/wrap.html\"><$c$c>strand.wrap.这将返回一个新的处理程序,将通过派遣链

  • Posting或<一个href=\"http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/io_service__strand/dispatch.html\">dispatching直接通过链。

  • Wrapping the handler with strand.wrap. This will return a new handler, that will dispatch through the strand.
  • Posting or dispatching directly through the strand.

组成操作是在中间来电来的流独特的是处理的的链中调用,如果一个人是present,而不是链中该构成的操作被启动。当相对于其他操作,这presents指定了链,其中的反转。下面是一些示例code着重于链的使用,这将表明,从经由非组成操作中读取一个插座,并同时写入到具有一个组成的操作。

Composed operations are unique in that intermediate calls to the stream are invoked within the handler's strand, if one is present, instead of the strand in which the composed operation is initiated. When compared to other operations, this presents an inversion of where the strand is specified. Here is some example code focusing on strand usage, that will demonstrate a socket that is read from via a non-composed operation, and concurrently written to with a composed operation.

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的使用参数依赖查找(ADL)通过调用中间处理在完成处理的链。因此,重要的是在完成处理程序的类型具有适当<一href=\"http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asio_handler_invoke.html\"><$c$c>asio_handler_invoke()挂钩。如果发生到不具备相应的 asio_handler_invoke()挂钩,如箱类型类型擦除,其中一个的boost ::功能从返回类型 strand.wrap 构建,那么中间处理程序将执行外链的,只有完成处理将在链内执行。请参见回答的更多细节。


Importance of Handler Types

Also, within composed operations, Boost.Asio uses argument dependent lookup (ADL) to invoke intermediate handlers through the completion handler's strand. As such, it is important that the completion handler's type has the appropriate asio_handler_invoke() hooks. If type erasure occurs to a type that does not have the appropriate asio_handler_invoke() hooks, such as a case where a boost::function is constructed from the return type of strand.wrap, then intermediate handlers will execute outside of the strand, and only the completion handler will execute within the strand. See this answer for more details.

在以下code,所有中间处理程序和完成处理程序将在链中执行:

In the following code, all intermediate handlers and the completion handler will execute within the strand:

boost::asio::async_write(stream, buffer, strand.wrap(&handle_write));

在以下code,仅完成处理程序将铸坯内执行。中间处理程序都不会在链中执行:

In the following code, only the completion handler will execute within the strand. None of the intermediate handlers will execute within the strand:

boost::function<void()> handler(strand.wrap(&handle_write));
boost::asio::async_write(stream, buffer, handler);


<分> 1。在<一个href=\"http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/history.html#boost_asio.history.asio_1_4_0___boost_1_37\">revision历史记录异常来此规则。如OS支持,同步的读,写,接受和连接操作是线程安全的。我一个包括在这里的完整性,但使用它谨慎建议。


1. The revision history documents an anomaly to this rule. If supported by the OS, synchronous read, write, accept, and connection operations are thread safe. I an including it here for completeness, but suggest using it with caution.

这篇关于为什么我需要每个连接搁浅使用boost :: ASIO的时候?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆