asio::steady_timer 不适用于完成处理程序“use_awaitable”

问题描述 投票:0回答:1

如果我在另一个协程中修改计时器,我很困惑带有完成处理程序“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 上的另一个协程中恢复它。

c++ boost-asio c++20 coroutine asio
1个回答
0
投票

文档声明

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<>
实例绑定共享取消槽:

住在Coliru

#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
© www.soinside.com 2019 - 2024. All rights reserved.