我可以在不重叠的情况下将多个协程生成到同一链吗?
Can I spawn multiple coroutines to the same strand without them overlapping?
我正在尝试对同一个 boost::asio::io_context::strand
调用 boost::asio::spawn
两次,每次都传递一个协程,我希望这两个协程一个接一个地执行,但它们却执行在平行线。这是说明这一点的代码:
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
在实际代码中,第一个协程设置套接字(执行 resolving/connecting/handshaking 的动作),第二个执行 send/receive。我的意图是 "append" 第二个协程到 strand 并让它仅在第一个协程完成后才开始执行。
如何实现这种效果?
编辑:更多上下文。第一个协程在构造函数中,第二个协程在成员函数中。如果我想让用户写
Foo foo;
foo.bar();
如何确保构造函数中的协程在 bar() 中的协程开始之前完成?
strand
s只保证它们的函数不会同时在多个线程上执行。这使您无需使用锁。
它们不会使单独的函数按顺序执行。如果您想要顺序执行,只需在第一个函数的末尾调用第二个函数:
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()
。
我正在尝试对同一个 boost::asio::io_context::strand
调用 boost::asio::spawn
两次,每次都传递一个协程,我希望这两个协程一个接一个地执行,但它们却执行在平行线。这是说明这一点的代码:
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
在实际代码中,第一个协程设置套接字(执行 resolving/connecting/handshaking 的动作),第二个执行 send/receive。我的意图是 "append" 第二个协程到 strand 并让它仅在第一个协程完成后才开始执行。
如何实现这种效果?
编辑:更多上下文。第一个协程在构造函数中,第二个协程在成员函数中。如果我想让用户写
Foo foo;
foo.bar();
如何确保构造函数中的协程在 bar() 中的协程开始之前完成?
strand
s只保证它们的函数不会同时在多个线程上执行。这使您无需使用锁。
它们不会使单独的函数按顺序执行。如果您想要顺序执行,只需在第一个函数的末尾调用第二个函数:
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()
。