繁体   English   中英

协程不分布在 asio::thread_pool 线程上

[英]Coroutines are not distributed over asio::thread_pool threads

我想尝试与 asio 一起使用 c++20 协程。 在一个简单的测试中,三个是协程,它们将在具有 4 个线程的 asio::thread_pool 上执行。 当我运行测试时,所有协程都一个接一个地执行,而不是同时执行。 这不是我预期的行为。 我认为 asio::thread_pool 的执行者会将协程分布在多个线程上。 有什么我忽略的吗?

using namespace asio::experimental::awaitable_operators;

asio::awaitable<std::string> one() {
    std::this_thread::sleep_for( 1s );
    co_return "egy";
}

asio::awaitable<std::string> two() {
    std::this_thread::sleep_for( 1s );
    co_return "ketto";
}

asio::awaitable<std::string> three() {
    std::this_thread::sleep_for( 1s );
    co_return "harom";
}

asio::awaitable<void> run_all() {
    auto [first, second, third] = co_await( one() &&
                                            two() &&
                                            three() );
}

int main() {
    asio::thread_pool pool( 4 );
    co_spawn( pool, run_all(), asio::detached );
    pool.join();

    return 0;
}

运行示例: https://godbolt.org/z/affo4EvWb

这将创建一个协程并将其分发到线程池中的线程。 线程池的 Schedular 会在这里工作。

co_spawn( pool, run_all(), asio::detached );

现在你在线程池的一个线程中,协程上下文将在这个线程中运行,线程池的调度在这里不起作用

代替线程池的调度,协程的调度将在这里工作 而这个调度将只在同一个线程中运行

asio::awaitable<void> run_all()
{
auto [first, second, third] = co_await( one() && two() && three() );
}

运算符&&是什么?

它在调用者的执行程序中创建 3 个协程,运行所有协程并等待它们完成。

所以协程 function onetwothree将在与run_all相同的执行器中运行。

在协程 function 中,可以通过co_await asio::this_coro::executor

这就是为什么它们在同一个线程中运行。

请记住,协程 function 不应被阻塞。 协程的调度在用户代码中运行,而不是在操作系统中。 function std::this_thread::sleep_for只会阻塞线程,协程的调度程序无法执行任何操作。

asio::awaitable<std::string> three() {
    std::this_thread::sleep_for( 1s );
    co_return "harom";
}

如何让协程调度知道你要让出什么? 您需要调用异步 function。

asio::awaitable<std::string> echo(std::string_view sv) {
    asio::steady_timer timer(co_await asio::this_coro::executor);
    timer.expires_after(1s);
    co_await timer.async_wait(asio::use_awaitable);
    co_return sv;
}

你可以在这里看到结果。 关联

在代码co_await clk.async_wait_for(1s)中。 你通知你想要 1s 的时间表。 它是有效的。

请记住,如果您在没有 co_await 或 co_return 的情况下调用 function,则它一定是阻塞调用。

ASIO 提供许多异步 I/O function,如定时器、文件、套接字、ssl、pipe、通道、串口和信号。 那些 function 有一个异步前缀。

顺便说一句,如果您想定义自己的异步调用,请参阅此示例:

using namespace asio;
static auto async_yield(auto&& handler)
{
    return async_initiate<decltype(handler), void(std::error_code)>(
        [](auto&& handler) {
            auto executor = get_associated_executor(handler);
            auto state = cancellation_state(get_associated_cancellation_slot(handler));
            post(executor, [handler = std::move(handler), state = std::move(state)]() mutable {
                std::error_code e;
                if (state.cancelled() != cancellation_type::none)
                    e = error::operation_aborted;
                handler(e);
            });
        }, handler
        );
}

asio::awaitable<void> foo()
{
   while(true)
   {
       co_await async_yield(asio::use_awaitable); //just like std::this_thread::yield(), but work in ASIO's coroutine context.
   }
}

重计算的另一个例子:

//a heavy calculation work 10s in another thread.
asio::thread_pool pool;
using namespace std::chrono;
using namespace asio;
auto async_calculation(auto&& handler)
{
    return async_initiate<decltype(handler), void(std::error_code e,size_t)>(
        [](auto&& handler) {
            auto slot = get_associated_cancellation_slot(handler);
            auto state = cancellation_state(get_associated_cancellation_slot(handler));
            auto executor = get_associated_executor(handler);
            post(pool, [executor = std::move(executor), handler = std::move(handler), state = std::move(state)]{
                size_t answer=0;
                auto now = steady_clock::now();
                while (state.cancelled() == cancellation_type::none && steady_clock::now() - now < 10s)
                {
                    answer+=1;
                }
                post(executor,[handler = std::move(handler), state = std::move(state), answer]() mutable {
                    std::error_code e;
                    if (state.cancelled() != cancellation_type::none)
                        e = error::operation_aborted;
                    handler(e,answer);
                });
            });
        }, handler
        );
}
asio::awaitable<void> foo()
{
   //this calculation won't block the coroutine.
   size_t answer=co_await async_calculation(asio::use_awaitable);
}

这对作者来说很脏,但对用户来说很漂亮。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM