【问题标题】:How to wait for completion of all boost:asio's stackful coroutines?如何等待所有 boost:asio 的堆栈协程完成?
【发布时间】:2014-12-16 12:05:28
【问题描述】:

我正在使用 asio::spawn 启动一些协程,我想等到所有这些都完成后再做一些其他工作。怎么做?

控制流程如下:

asio::spawn (io, [] (asio::yield_context yield) {
  ...
  // starting few coroutines
  asio::spawn (yield, [] (asio::yield_context yield2) { ... });
  asio::spawn (yield, [] (asio::yield_context yield2) { ... });
  asio::spawn (yield, [] (asio::yield_context yield2) { ... });
  asio::spawn (yield, [] (asio::yield_context yield2) { ... });

  // now I want to wait for all of them to finish before I do 
  // some other work?
  ...
});

io.run ();

更新

下面是示例代码

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>

#include <iostream>
using namespace std;

int main ()
{
  using namespace boost::asio;

  io_service io;

  spawn (io, [&] (yield_context yield) {
    cout << "main coro starts\n";

    auto lambda = [&] (yield_context yield)
      {
        cout << "in lambda inside subcoroutine - starts\n";
        steady_timer t (io, std::chrono::seconds (1));
        t.async_wait (yield);
        cout << "in lambda inside subcoroutine - finishes\n";
      };

    // starting few coroutines
    spawn (yield, lambda);
    spawn (yield, lambda);

    // now I want to wait for all of them to finish before I do
    // some other work?
    // ???

    cout << "main coro finishes\n";
  });

  io.run ();
}

输出是:

// main coro starts
// in lambda inside subcoroutine - starts
// in lambda inside subcoroutine - starts
// main coro finishes <----
// in lambda inside subcoroutine - finishes
// in lambda inside subcoroutine - finishes

虽然我期待:

// main coro starts
// in lambda inside subcoroutine - starts
// in lambda inside subcoroutine - starts
// in lambda inside subcoroutine - finishes
// in lambda inside subcoroutine - finishes
// main coro finishes

(见“主coro完成”行的地方)

【问题讨论】:

  • IIRC io.run (); 只是做你想做的事......
  • @vines - 不,不是。 Coliru Code

标签: c++ boost boost-asio coroutine


【解决方案1】:

我找到了一种……解决方法。

我可以使用一个无限持续时间的计时器,并从最后一个子协程中取消它。这将唤醒主协程。

Coliru Example

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>

#include <iostream>
using namespace std;

int main ()
{
  using namespace boost::asio;

  io_service io;

  spawn (io, [&] (yield_context yield) {
    cout << "main coro starts\n";


    steady_timer rendez_vous (io, steady_timer::clock_type::duration::max ());
    /* volatile */ int counter = 2;


    auto lambda = [&] (yield_context yield)
      {
        cout << "in lambda inside subcoroutine - starts\n";
        steady_timer t (io, boost::chrono::seconds (1));
        t.async_wait (yield);
        cout << "in lambda inside subcoroutine - finishes\n";

        if (--counter == 0)
            rendez_vous.cancel ();
      };

    // starting few coroutines
    spawn (yield, lambda);
    spawn (yield, lambda);

    // now I want to wait for all of them to finish before I do
    // some other work?
    // ???
    boost::system::error_code ignored_ec;
    rendez_vous.async_wait (yield [ignored_ec]);
    // ignore errors here by reason.

    cout << "main coro finishes\n";
  });

  io.run ();
}

坦率地说我不喜欢这个解决方案,因为它滥用了“计时器”的概念和对象,而且可能会浪费系统资源。

【讨论】:

  • 无论如何我都喜欢。滥用volatile除外。如果您希望它是线程安全的,请使用atomic_int。否则就留下int
  • 对于它的价值,当我有类似的需要时,我也使用了一个计时器。它提供了一个相当可移植的解决方案,并且通过一些抽象实现的更高级别的类,它提供了一个相当干净的解决方案。我相信counter 可以是普通的int,因为所有这些协程都将在具有适当内存屏障的相同strand 中运行。
【解决方案2】:

更好的选择是使用纤维(boost.fiber 集成到 boost.asio)。 boost::fiber 是协程 + 调度器 + 同步类(API 像 std::thread),可以像协程一样在 boost.asio 上下文中使用。

【讨论】:

  • 我知道 boost.fiber,它非常有趣。但它准备好尝试生产任务了吗?您个人对此有何看法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
  • 1970-01-01
  • 1970-01-01
  • 2015-05-27
  • 2010-09-20
相关资源
最近更新 更多