R.J*_*R.J 3 c++ boost-asio c++20 c++-coroutine
在下面的代码中,会话协程的参数是通过引用传递的。
#include <boost/asio.hpp>
#include <iostream>
boost::asio::awaitable<void> session(const std::string& name)
{
std::cout << "Starting " << name << std::endl;
auto executor = co_await boost::asio::this_coro::executor;
}
int main()
{
boost::asio::io_context io_context;
co_spawn(io_context, session("ServerA"), boost::asio::detached);
co_spawn(io_context, session("ServerB"), boost::asio::detached);
io_context.run();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
由于某种我不明白的原因,上面的代码会导致打印Starting ServerB两次。
> g++ -std=c++20 ../test-coro.cpp -o test-coro && ./test-coro
Starting ServerB
Starting ServerB
Run Code Online (Sandbox Code Playgroud)
但是当我将协程参数更改为按值传递时,它将正确打印Starting ServerA和Getting ServerB
#include <boost/asio.hpp>
#include <iostream>
boost::asio::awaitable<void> session(std::string name)
{
std::cout << "Starting " << name << std::endl;
auto executor = co_await boost::asio::this_coro::executor;
}
int main()
{
boost::asio::io_context io_context;
co_spawn(io_context, session("ServerA"), boost::asio::detached);
co_spawn(io_context, session("ServerB"), boost::asio::detached);
io_context.run();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
> g++ -std=c++20 ../test-coro.cpp -o test-coro && ./test-coro
Starting ServerA
Starting ServerB
Run Code Online (Sandbox Code Playgroud)
这是预期的还是这是编译器/库错误?如果这是预期的,那么其原因是什么?
环境:
Arch Linux 5.18.16-arch1-1
gcc(GCC)12.2.0
boost版本1.79
您可以将协程状态视为包含函数调用堆栈上的内容(这使得函数可恢复):cppreference
当协程开始执行时,它会执行以下操作:
- 使用运算符 new 分配协程状态对象(见下文)
- 将所有函数参数复制到协程状态:按值参数被移动或复制,按引用参数保留引用(因此如果在引用对象的生命周期结束后恢复协程,则可能会变为悬空)
协程逻辑上存储对临时字符串的引用。哎呀。
我还没有检查过,但我假设 Asio 的可等待实现以初始值开始suspend_always(这对我来说对于执行器模型来说很直观)。
是的,这意味着co_spawn任何引用参数都意味着必须保证所引用对象的生命周期。
在我的系统上,输出只是
Starting
Starting
Run Code Online (Sandbox Code Playgroud)
一种修复方法就是您所展示的。为了说明生命周期方面:
{
std::string a="ServerA", b="ServerB";
co_spawn(io_context, session(a), boost::asio::detached);
co_spawn(io_context, session(b), boost::asio::detached);
io_context.run();
}
Run Code Online (Sandbox Code Playgroud)
也是一个有效的修复。对于这个简单的例子,我建议std::string_view无论如何:
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
boost::asio::awaitable<void> session(std::string_view name)
{
std::cout << "Starting " << std::quoted(name) << std::endl;
auto executor = co_await boost::asio::this_coro::executor;
}
int main()
{
boost::asio::io_context io_context;
std::string const a="ServerA";
co_spawn(io_context, session(a), boost::asio::detached);
co_spawn(io_context, session("ServerB"), boost::asio::detached);
io_context.run();
}
Run Code Online (Sandbox Code Playgroud)
印刷
Starting "ServerA"
Starting "ServerB"
Run Code Online (Sandbox Code Playgroud)