如何解决co_spawn被co_await-ed后阻塞了ASIO链
我一直在试验ASIO链和C ++ 20协程,并注意到奇怪的行为。当我在一条链上生成一个协程时,在它的第二条链上生成另一个协程,然后等待它,当控件返回到外部协程时,两条链都被阻塞。这是预期的行为吗?如果是这样,我该如何解决?
以下代码演示了该问题,并在Linux上使用boost.asio,boost版本1.74.0,gcc版本10.2.0进行了测试。
#include <thread>
#include <chrono>
#include <iostream>
#include <boost/asio.hpp>
namespace asio = boost::asio;
using namespace std::chrono_literals;
int main() {
auto ioc = asio::io_context{};
// Create two strands
auto s1 = asio::make_strand(ioc);
auto s2 = asio::make_strand(ioc);
// Prevent the io context from stopping
auto wg = asio::make_work_guard(ioc);
// Over-provision threads just in case
auto w1 = std::jthread{[&]() { ioc.run(); }};
auto w2 = std::jthread{[&]() { ioc.run(); }};
auto w3 = std::jthread{[&]() { ioc.run(); }};
auto w4 = std::jthread{[&]() { ioc.run(); }};
auto w5 = std::jthread{[&]() { ioc.run(); }};
asio::co_spawn(s1,[&]() -> asio::awaitable<void> {
std::cout << "a" << std::endl;
co_await asio::co_spawn(s2,[]() -> asio::awaitable<void> {
// Works as intended
// s2 is blocked,s1 is free (b will print)
std::this_thread::sleep_for(2s);
co_return;
},asio::use_awaitable);
// Expected: s1 is blocked,s2 is free (d will print,e will wait 5s)
// Reality: both s1 and s2 are blocked (after 5s,d and e print at the same time).
// Why?
std::cout << "c" << std::endl;
std::this_thread::sleep_for(5s);
},asio::detached);
std::this_thread::sleep_for(1s);
asio::co_spawn(s1,[]() -> asio::awaitable<void> {
std::cout << "b" << std::endl;
co_return;
},asio::detached);
std::this_thread::sleep_for(2s);
asio::co_spawn(s2,[]() -> asio::awaitable<void> {
std::cout << "d" << std::endl;
co_return;
},asio::detached);
asio::co_spawn(s1,[]() -> asio::awaitable<void> {
std::cout << "e" << std::endl;
co_return;
},asio::detached);
std::this_thread::sleep_for(10s);
ioc.stop();
}
预期行为:
t__0_1_2_3_4_5_6_7
s1 a_b_c_________e
s2 ______d________
实际行为:
t__0_1_2_3_4_5_6_7
s1 a_b_c_________e
s2 ______________d
解决方法
想出了一个解决方案:与其在链上生成协程,不如在协程中生成协程,并在需要通过该链访问时发布到链上。
asio::co_spawn(ioc,[&]() -> asio::awaitable<void> {
co_await asio::post(s1,asio::use_awaitable);
std::cout << "a" << std::endl;
co_await asio::post(s2,asio::use_awaitable);
std::this_thread::sleep_for(2s);
co_await asio::post(s1,asio::use_awaitable);
std::cout << "c" << std::endl;
std::this_thread::sleep_for(5s);
},asio::detached);
,
boost-asio模型在stateful
协程 [docs]中产生一条链。因此,您的代码的行为恰恰是它应该如何工作的(即,以同步方式)。
您的假设可能对stateless
协程是正确的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。