簡體   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