【问题标题】:How does boost::asio::io_service prioritize work?boost::asio::io_service 优先级如何工作?
【发布时间】:2012-05-18 20:00:38
【问题描述】:

我正在使用boost::asio::io_service 来管理一些异步 TCP 通信。这意味着我创建了一个boost::asio::ip::tcp::socket 并将io_service 给它。当我开始交流时,它的示意图如下:

Async Resolve -> Callback -> Async Connect -> Callback -> Async Write -> Callback -> Async Read

我省略了解析和绑定等部分。假设 Socket 已经绑定到一个端口并且主机名被解析(所以 connect 意味着建立到端点的真实连接)

现在的重点是我可以使用相同的io_service 对象启动多个异步连接。这意味着,例如,虽然在我的io_service 线程中,程序即将向Async Write 发送一些数据,但主线程将调用Async Resolve on Socket(但使用相同的io_service)。 这意味着我的io_service 现在有一些并行工作要做 - 我想知道它会如何优先处理这些工作?

例如是这样的

Main Thread              |      io_service Thread
-------------------------+-----------------------------------------------
SocketA->Async Connect   |
//Some other Stuff       |     SocketA->Callback from Async Connect
                         |     SocketA->Async Write
SocketB->Async Connect   |      
                         |     --> ?

现在我不得不承认我不太确定io_service 是如何工作的。在第四行中,现在需要执行两个不同的异步函数。

io_service 是否能够同时执行Async ConnectAsync Write?如果是这种情况,很明显总是会调用首先完成的函数的回调。

如果io_service有能力这样做,它将按什么顺序工作?如果SocketA Async Write会先被调用,它的回调也会先被调用。实际上,在 SocketA 上的整个操作完成之前,总会有工作。

编辑:

根据ereOns 的评论,我试图让我的问题更准确一点:

io_service 线程的角度来看——SocketA Async Connect 调用是异步的还是同步的?从我的主线程的角度来看,它当然是异步的(它只是调度命令然后继续)。但是在io_service 线程中这个特定的Connect 调用会阻塞其他操作吗?

换句话说:一个io_service 是否能够在读取另一个套接字时连接到一个套接字?

另一个例子是,如果我在我的主函数中一个接一个地调用 2 Async Connect

SocketA->AsyncConnect();
SocketB->AsyncConnect();

假设来自 SocketA 的主机有点慢,需要两秒钟才能回答。因此,当 SocketA 尝试连接时,SocketB 会同时连接还是必须等到 SocketA 完成/超时?

【问题讨论】:

  • 我不确定是否能完全理解您的问题,但我会说 Asio 会尽快以未指定的顺序引发连接或写入事件,以先到者为准。如果我可以问,为什么你认为顺序很重要?
  • @ereOn 我在问题中添加了一些内容以使其更精确

标签: c++ sockets boost boost-asio


【解决方案1】:

所有工作都在 io_service.run() 运行的线程中完成。

但是,对任何 async_ 方法的调用不会阻塞这个特定线程:它的行为与 io_service.run() 在多个事件中调用 select() 并“返回”(调用回调),只要引发此类事件。也就是说,如果你调用:

socketA->async_connect();
socketB->async_connect();

socketB 也可以在socketA 之前连接,然后将首先调用关联的回调,仍然在线程io_service.run() 运行中。

这就是 Boost Asio 的全部优点:它会在更合适的时候非常小心地处理轮询、等待和引发事件,从而为您留下“简单”的部分。

【讨论】:

  • 好的,这是“真正的”异步,这意味着async_ 方法可以并行工作。首先调用哪个回调取决于 async_ 函数中的哪个首先“返回”?我做对了吗?
  • 由于“this”的新答案,我需要再次问:我们是从IO_Service Thread的角度讲的。我很清楚主线程中的 async_call 将立即返回。然而,回调将在 io_service 线程中调用......对吗?!
  • @Toby:在io_service 线程中看到它的连接调用是非阻塞的。当您在套接字上调用async_connect() 时,它实际上只是将调用推送到一个列表,该列表将被io_service::run() 清空。这保证了您可以从任何线程调用async_connect。但是,这并不意味着呼叫被阻塞。 async_connect() 将创建一个事件,io_service::run() 将等待他知道的所有事件,如果连接成功,操作系统将向run() 发出信号,然后run() 将在其自己的线程中调用适当的回调.
【解决方案2】:

您不应该在这里尝试预测异步操作的执行顺序。 async_connect 只是向io_service 发出信号并立即返回。真正的工作在 io_service 对象的事件处理循环 (io_service::run) 中完成,但您不知道具体细节。它很可能使用特定于操作系统的异步 IO 函数。

尚不清楚您要达到的目标。也许你应该使用同步操作。也许您应该使用线程同步功能。 也许io_service::run_one 会帮助你(它最多执行一个处理程序)。

也许您想在不同的线程中多次调用io_service::run,从而创建一个线程池。这样一来,一个长完成处理程序就不会阻塞所有其他处理程序。

boost::asio::io_service service;
const size_t ASIO_THREAD_COUNT = 3;
boost::thread_group threadGroup;
for (size_t i = 0; i < ASIO_THREAD_COUNT; ++i)
        threadGroup.create_thread(boost::bind(&boost::asio::io_service::run,
            &service, boost::system::error_code()));

【讨论】:

  • 这是错误的。如果 io_service::run() 只是在被驱逐的线程中进行阻塞调用,那么 Boost Asio 的意义何在?您不需要为此创建多个线程,更不用说线程池了。你正在制造一个恐怖来解决一个从未存在过的问题。
  • 也许我假设了太多,同时试图说一个人不应该假设太多。但众所周知,处理程序是从run-thread 调用的。我不确定你不喜欢线程池的什么,Boost.Asio 的一切都很好,很整洁:引用doc:Multiple threads may call io_service::run() to set up a pool of threads from which completion handlers may be invoked
  • 在需要时使用线程池没有问题。但是对于这里的特定情况,这有点矫枉过正,因为 Boost Asio 旨在同时处理异步事件。
猜你喜欢
  • 2019-01-30
  • 2015-04-22
  • 1970-01-01
  • 2011-06-16
  • 2017-09-17
  • 2013-08-07
  • 1970-01-01
  • 2012-11-26
  • 2011-12-18
相关资源
最近更新 更多