如何解决与 ASIO 协程和网络 TS 与 C++20 的同步和结果检索
我正在试验 asio 无堆栈协程和新引入的用于 co_return
、co_await
的 C++20 构造。
我浏览了示例并根据我的理解提出了一些问题,因为我仍然看到与竞争条件相关的崩溃:(
示例:(为了简单起见,我将省略所有异常处理)
awaitable<size_t> echo_request(tcp::socket& socket)
{
using namespace boost::asio;
using namespace std::literals;
char data[3] = {};
co_await async_write(socket,buffer("abc"sv),use_awaitable);
size_t n = co_await async_read(socket,buffer(data,sizeof(data)),transfer_all(),use_awaitable);
co_return n;
}
int main()
{
// calling the coroutines
using namespace boost::asio;
auto ctx = io_context{};
auto sockets = vector<tcp::socket>{};
for(auto i{0}; i<20; ++i)
sockets.push_back(make_connection(...));
auto strand = make_strand(ctx);
for(auto&& socket : sockets)
co_spawn(strand,echo_request(),detached);
auto th = std::thread([&]{ ctx.run(); });
ctx.run();
th.join();
}
我知道 ctx.run()
将执行与当前任务一样多的次数。因为在上面的例子中 co_spawn
被调用了 20 次,每次调用 2 co_wait
,所有 ctx.run()
结果的总和将为 40。但是,这里 awaitable<size_t>
返回一个结果。如何从 main
函数中检索此结果?
我看到一个示例,其中在 lambda 函数中使用了 std::promise
而不是 detached
awaitable:
std::promise<size_t> p;
co_spawn(strand,[&p](auto&& err,auto&& value)
{
if(err) p.set_exception(err);
else p.set_value(value);
});
std::promise
可能在内部使用互斥锁(实际上它确实使用了它)。有没有更轻量级的替代品?
最后,如果任何 echo_request
函数修改非原子值会发生什么(它可能是一个更复杂的结构,如哈希表,但这里只是一个全局变量的 int 增量)。
示例:
int total_requests = 0;
awaitable<size_t> echo_request(tcp::socket socket)
{
using namespace boost::asio;
using namespace std::literals;
char data[3] = {};
co_await async_write(socket,use_awaitable);
++total_requests; // THIS IS A RACE CONDITION
size_t n = co_await async_read(socket,use_awaitable);
++total_requests; // ANOTHER RACE CONDITION
co_return n;
}
我的理解是,如果异步操作在 strand
内执行,它们将被线性化(没有顺序保证)。基本上,即使从多个线程运行 io_context::run()
也能确保在一个链中执行的可等待对象之间没有并发?我原以为下面的修改会解决这个问题,但它没有。
此外,只有标有 co_await
的地方才是中断点。因此,如果多个函数实例在 co_await
之外的同一条链中运行,则不会中断。基本上,标有 // THIS IS A RACE CONDITION
的代码片段最终不能由 2 个线程并发执行,并且增加全局变量是安全的。这是正确的吗?
如果不是这样,下面的代码片段会阻止竞争条件吗?
int total_requests = 0;
awaitable<size_t> echo_request(boost::asio::strand& strand,boost::asio::tcp::socket& socket)
{
using namespace boost::asio;
using namespace std::literals;
char data[3] = {};
co_await async_write(socket,use_awaitable);
strand.execute([&] { ++total_reqeusts; }); // WOULD THAT PREVENT THE RACE CONDITION???
size_t n = co_await async_read(socket,use_awaitable);
strand.execute([&] { ++total_requests; });
co_return n;
}
解决方法
ctx.run
将一直运行,直到 io_context 上的所有工作完成。所以如果你删除这一行
auto th = std::thread([&]{ ctx.run(); });
您会将应用程序转换为单线程异步应用程序。那么你就不需要担心股线等。
作为单线程应用程序,可以将请求或字节与全局变量相加。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。