如果我在另一个协程中修改计时器,我很困惑带有完成处理程序“use_awaitable”的 asio::timer 协程不起作用。
“离开 foo”从未到达:
Enter foo
Enter bar
Leave bar
The End
下面是代码:
static asio::awaitable<void> foo(asio::steady_timer& timer)
{
cout << "Enter foo" << endl;
timer.expires_from_now(asio::steady_timer::clock_type::duration::max());
co_await timer.async_wait(asio::use_awaitable);
cout << "Leave foo" << endl;
}
static asio::awaitable<void> bar(asio::steady_timer& timer)
{
cout << "Enter bar" << endl;
sleep(2); // wait a little for asio::io_service::run to be executed
timer.expires_after(asio::chrono::seconds(1));
cout << "Leave bar" << endl;
co_return;
}
int main()
{
asio::io_context ioService;
asio::steady_timer timer(ioService);
asio::co_spawn(ioService, foo(timer), asio::detached);
asio::co_spawn(ioService, bar(timer), asio::detached);
ioService.run();
std::printf("The End\n");
return 0;
}
实际上我只是想挂起一个协程并在没有任何其他线程的情况下在 asio/c++20 上的另一个协程中恢复它。
文档声明
expires_after
取消任何未决的异步等待。使用BOOST_ASIO_ENABLE_HANDLER_VIZ
:
你不处理异常。如果你这样做了,你会注意到它发生了:
static asio::awaitable<void> foo(asio::steady_timer& timer) try {
std::cout << "Enter foo" << std::endl;
timer.expires_from_now(asio::steady_timer::clock_type::duration::max());
co_await timer.async_wait(asio::use_awaitable);
std::cout << "Leave foo" << std::endl;
} catch (boost::system::system_error const& se) {
std::cout << "Error: " << se.what() << std::endl;
}
现在输出是
Enter foo
Enter bar
Leave bar
Error: Operation canceled [system:125]
The End
或者你可以使用错误代码:
boost::system::error_code ec;
co_await timer.async_wait(redirect_error(asio::use_awaitable, ec));
std::cout << "Leave foo (" << ec.message() << ")" << std::endl;
甚至:
auto [ec] = co_await timer.async_wait(as_tuple(asio::use_awaitable));
std::cout << "Leave foo (" << ec.message() << ")" << std::endl;
双面印刷(Live):
Enter foo
Enter bar
Leave bar
Leave foo (Operation canceled)
The End
coro 中的
sleep
-ing 有问题。它会阻塞执行上下文,因此无法完成任何异步工作。那当然不是你想要的。
此外,您看起来很像是在尝试协调两个协程的执行。如果您想要“类似信号”的行为,那么无论如何,只要您也正确处理错误代码,它就可以工作。
如果您只想协调取消,请使用
cancellation_slot
设施。事实上,存在非常强大的语法糖用于跨 asio::awaitable<>
实例绑定共享取消槽:
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using namespace asio::experimental::awaitable_operators;
static void trace(char const* msg) {
static constexpr auto now = std::chrono::steady_clock::now;
static auto const start = now();
std::cout << std::setw(4) << (now() - start) / 1ms << "ms " << msg << std::endl;
}
static asio::awaitable<void> async_simulate_work(auto delay) {
co_await asio::steady_timer(co_await asio::this_coro::executor, delay)
.async_wait(asio::use_awaitable);
}
static asio::awaitable<void> foo() try {
trace("Foo enter");
for (;;) {
trace("Foo working...");
co_await async_simulate_work(100ms);
}
trace("Foo leave");
} catch (boost::system::system_error const& se) {
trace("Foo cancel");
}
static asio::awaitable<void> bar() {
trace("Bar enter");
co_await async_simulate_work(260ms);
trace("Bar leave");
}
int main() {
asio::io_context ioService;
asio::steady_timer timer(ioService);
asio::co_spawn(ioService, foo() || bar(), asio::detached);
ioService.run();
trace("The End");
}
印刷例如
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
0ms Foo enter
0ms Foo working...
0ms Bar enter
100ms Foo working...
200ms Foo working...
260ms Bar leave
260ms Foo cancel
260ms The End