提升::ASIO。确保仅在调用 async_receive(...) 后调用 io_service.run() ?

Posted

技术标签:

【中文标题】提升::ASIO。确保仅在调用 async_receive(...) 后调用 io_service.run() ?【英文标题】:BOOST::ASIO. Making sure io_service.run() is invoked only after async_receive(...) is called? 【发布时间】:2017-07-05 20:00:07 【问题描述】:

我有一个问题,两个线程是这样调用的,一个接一个。

new boost::thread( &SERVER::start_receive, this);

new boost::thread( &SERVER::run_io_service, this);

第一个线程调用这个函数的地方。

void start_receive()
   
    udp_socket.async_receive(....);

第二个线程调用,

void run_io_service()
  
    io_service.run();

有时io_service 线程在start_receive() 线程之前完成,然后服务器无法接收数据包。

我曾考虑在两个线程之间放置一个睡眠函数,以等待一段时间以等待start_receive() 完成,这很有效,但我想知道是否有另一种可靠的方法来实现这一点?

【问题讨论】:

没有必要将 async_receive 放在自己的线程中。 asio 是异步的 好的,不知道,使用这个框架还是新手,但是异步函数只能在io_service线程之前工作吗?换句话说,一个保证在另一个之前? 异步操作会导致 io_service::work 处于活动状态,直到调用完成处理程序。因此 io_service::run 调用在调用完成处理程序之前不会返回。 io_service::run 在调用完成处理程序后返回时,它会停止阻塞并退出吗?对我来说,async_receive 似乎可以在程序期间继续接受数据包,而io_service::run 仅在启动时初始化? 查看我对前置/后置条件的回答。您需要阅读文档几次才能理解。我花了很长时间才明白这一切。 【参考方案1】:

当您调用io_service.run() 时,线程将阻塞,调度已发布的处理程序,直到:

    没有与io_service 关联的io_service::work 对象,或者

    io_service.stop() 被调用。

如果发生上述任何一种情况,io_service 将进入停止状态,并在未来拒绝调度任何处理程序,直到调用其 reset() 方法。

每次对与 io_service 关联的 io 对象启动异步操作时,都会在异步处理程序中嵌入一个 io_service::work 对象。

因此,在异步处理程序运行之前,上述第 (1) 点不会发生。

因此,此代码将保证异步过程完成并且断言通过:

asio::io_service ios;    // ios is not in stopped state
assert(!ios.stopped());
auto obj = some_io_object(ios);
bool completed = false;
obj.async_something(..., [&](auto const& ec)  completed = true; );
// nothing will happen yet. There is now 1 work object associated with ios
assert(!completed);

auto ran = ios.run();
assert(completed);
assert(ran == 1);    // only 1 async op waiting for completion.
assert(ios.stopped());  // io_service is exhausted and no work remaining

ios.reset();
assert(!ios.stopped());  // io_service is ready to run again 

【讨论】:

很好的解释,ASIO 开发者应该使用这个解释而不是这个。 boost.org/doc/libs/1_64_0/doc/html/boost_asio/reference/… 我一直觉得asio是一个杰作,但是文档让我们失望了。【参考方案2】:

如果您想保持io_service 运行,请创建一个work 对象:

boost::asio::io_service svc;
auto work = std::make_shared<boost::asio::io_service::work>(svc);
svc.run(); // this will block as long as the work object is valid.

这种方法的好处是上面的work 对象将保持svc 对象“运行”,但不会阻止对其进行的任何其他操作。

【讨论】:

你的意思是我可以在async_recieve 调用之前或之后调用它? 不需要用指针来控制工作对象的生命周期。如果你想让 io 服务停止,只需调用 stop() 就可以了。 我已经有一个menu 线程允许io_service 停止和启动,但这是一个有趣的花絮,谢谢!

以上是关于提升::ASIO。确保仅在调用 async_receive(...) 后调用 io_service.run() ?的主要内容,如果未能解决你的问题,请参考以下文章

提升 Asio Peek 和完成条件

提升 asio 绑定:错误的文件描述符

提升 asio 行为 - 从多个线程调用 ios_service::run

提升 Asio 单线程性能

内存 SPIKE - 提升 ASIO 异步读取

从套接字和 STDIN 提升 Asio 多线程