使用多个io_service对象

Rav*_*ugu 10 boost-asio

我的应用程序在其中监听和处理来自互联网套接字和unix域套接字的消息.现在我需要将SSL添加到互联网套接字,我正在io_service为应用程序中的所有套接字使用单个对象.现在看来我需要io_service为网络套接字和unix域套接字添加单独的对象.我没有在我的应用程序的任何线程,我用async_sendasync_recieveasync_accept处理数据和连接.请指出使用io_service具有异步处理程序的多个对象的任何示例.

Tan*_*ury 32

该问题存在一定程度的不确定性,好像io_service需要多个对象一样.我找不到参考文档中的任何内容,或者强制要求单独对象的SSLUNIX域套接字的概述io_service.无论如何,这里有几个选择:


单身io_service:

尝试使用单个io_service.

如果您没有io_service对象的直接句柄,但是您有一个Boost.Asio I/O对象的句柄,例如套接字,那么io_service可以通过调用获得关联对象的句柄socket.get_io_service().


使用一个线程io_service:

如果io_service需要多个对象,则将线程专用于每个对象io_service.此方法用于Boost.Asio的HTTP Server 2示例.

boost::asio::io_service service1;
boost::asio::io_service service2;

boost::thread_group threads;
threads.create_thread(boost::bind(&boost::asio::io_service::run, &service1));
service2.run();
threads.join_all();
Run Code Online (Sandbox Code Playgroud)

这种方法的一个结果是它可能需要由应用程序进行线程安全保证.例如,如果service1service2两个有调用完成处理message_processor.process(),则message_processor.process()需要或者是线程安全的或称为一个线程安全的方式.


民意调查io_service:

io_service提供非阻塞替代品run().在io_service::run()所有工作完成之前阻塞的地方 io_service::poll()将运行准备运行并且不会阻塞的处理程序.这允许单个线程在多个io_service对象上执行事件循环:

while (!service1.stopped() &&
       !service2.stopped())
{
  std::size_t ran = 0;
  ran += service1.poll();
  ran += service2.poll();
  // If no handlers ran, then sleep.
  if (0 == ran)
  {
    boost::this_thread::sleep_for(boost::chrono::seconds(1));
  }
}
Run Code Online (Sandbox Code Playgroud)

为了防止在没有可立即运行的处理程序时出现紧凑的循环,可能值得在睡眠中添加.请注意,此睡眠可能会在事件的整体处理中引入延迟.


将处理程序转移到单个io_service:

一种有趣的方法是使用a strand将完成处理程序转移到单个处理程序io_service.这允许一个线程per io_service,同时防止需要让应用程序进行线程安全保证,因为所有完成处理程序将通过单个服务发布,其事件循环仅由单个线程处理.

boost::asio::io_service service1;
boost::asio::io_service service2;

// strand2 will be used by service2 to post handlers to service1.
boost::asio::strand strand2(service1);
boost::asio::io_service::work work2(service2);

socket.async_read_some(buffer, strand2.wrap(read_some_handler));

boost::thread_group threads;
threads.create_thread(boost::bind(&boost::asio::io_service::run, &service1));
service2.run();
threads.join_all();
Run Code Online (Sandbox Code Playgroud)

这种方法确实会产生一些后果:

  • 它需要通过主要运行的处理程序io_service进行包装strand::wrap().
  • 异步链现在运行两年io_service,创造了额外的复杂性.重要的是要考虑次要io_service不再有工作,导致其run()返回的情况.

异步链通常在同一链中发生io_service.因此,该服务永远不会失去工作,因为完成处理程序将在其上发布额外的工作io_service.

   |    .------------------------------------------.
   V    V                                          |
read_some_handler()                                |
{                                                  |
  socket.async_read_some(..., read_some_handler) --'
}
Run Code Online (Sandbox Code Playgroud)

另一方面,当使用strand将工作转移到另一个时io_service,在其中调用包装的处理程序service2,使其将完成处理程序发布到其中service1.如果包装的处理程序是唯一的工作service2,则service2不再有工作,导致servce2.run()返回.

    service1                      service2
====================================================

        .----------------- wrapped(read_some_handler)
        |                            .
        V                            .
 read_some_handler                NO WORK
        |                            .
        |                            .
        '----------------> wrapped(read_some_handler)
Run Code Online (Sandbox Code Playgroud)

为了解释这一点,示例代码使用io_service::workfor,service2以便run()在明确告知之前保持阻塞状态stop().