我可以在不重叠的情况下将多个协同程序生成到同一链中吗?

时间:2018-09-09 16:14:56

标签: c++ boost-asio boost-coroutine

我正在尝试对同一个boost::asio::spawn调用boost::asio::io_context::strand两次,每次都传递一个协程,而我期望这两个协程一个接一个地执行,但是它们却并行执行。以下代码说明了这一点:

boost::asio::io_context ioc;
boost::asio::io_context::strand strand{ioc};
boost::asio::spawn(strand, [&](boost::asio::yield_context yield)
                   {
                       cout << "1\n";
                       ioc.post(yield);
                       cout << "2\n";
                       ioc.post(yield);
                       cout << "3\n";
                   });

boost::asio::spawn(strand, [&](boost::asio::yield_context yield)
                   {
                       cout << "10\n";
                       ioc.post(yield);
                       cout << "20\n";
                       ioc.post(yield);
                       cout << "30\n";
                   });
ioc.run();

这将输出:

1
10
2
20
3
30

当我期望的时候:

1
2
3
10
20
30

在实际代码中,第一个协程建立了一个套接字(进行解析/连接/握手的动作),第二个协程进行了发送/接收。我的意图是将第二个协程“附加”到该链上,并使其仅在第一个协程完成后才开始执行。

我怎么能达到这种效果?

编辑:更多上下文。第一个协程位于构造函数中,第二个协程位于成员函数中。如果我要允许用户写

Foo foo;
foo.bar();

如何在bar()中的协程开始之前确保构造函数中的协程完成?

1 个答案:

答案 0 :(得分:2)

strand仅保证不会在多个线程上同时执行其功能。这使您无需使用锁。

它们不会使单独的功能按顺序执行。如果要顺序执行,只需在第一个函数的末尾调用第二个函数:

boost::asio::io_context ioc;
boost::asio::io_context::strand strand{ioc};
auto main = [&](boost::asio::yield_context yield)
                   {
                       cout << "10\n";
                       ioc.post(yield);
                       cout << "20\n";
                       ioc.post(yield);
                       cout << "30\n";
                   };
boost::asio::spawn(strand, [&](boost::asio::yield_context yield)
                   {
                       cout << "1\n";
                       ioc.post(yield);
                       cout << "2\n";
                       ioc.post(yield);
                       cout << "3\n";
                       main();
                   });

如果您不能从第一个函数中调用第二个函数,那么我曾经使用过一种技术,那就是要执行一系列函数,因为所有内容都是一成不变的,无需担心锁定:

bool executing = false;
struct ExecuteLock
{
  ExecuteLock()
  {
    if ( !executing )
    {
      executing = true;
      locked = true;
    }
    else
    {
      locked = false;
    }
  }

  ~ExecuteLock()
  {
    if ( locked )
    {
      executing = false;
    }
  }

  bool locked;
};

typedef QueueFunction std::function<void(boost::asio::yield_context yield);

std::queue< QueueFunction > executeQueue;

void run( QueueFunction f )
{
  boost::asio::spawn( strand, [=](boost::asio::yield_context yield)
  {
    ExecuteLock lock;
    if (!lock.locked)
    {
      executeQueue.push( f );
      return;
    }
    f();
    while ( !executeQueue.empty() )
    {
      executeQueue.front()();
      executeQueue.pop();
    }
  } );
}

然后您每次想执行某件事时都可以致电run()