简体   繁体   English

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

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

I wanted to try out to c++20 coroutines together with asio.我想尝试与 asio 一起使用 c++20 协程。 In a simple test three are coroutines which would be executed on a asio::thread_pool with 4 threads.在一个简单的测试中,三个是协程,它们将在具有 4 个线程的 asio::thread_pool 上执行。 When I run the test all the coroutines are executed one-by-one after each other, and not simultaneously.当我运行测试时,所有协程都一个接一个地执行,而不是同时执行。 This is not the behavior that I expected.这不是我预期的行为。 I thought that the executor of asio::thread_pool would distribute the coroutines over multiple threads.我认为 asio::thread_pool 的执行者会将协程分布在多个线程上。 Is there something that I overlooked?有什么我忽略的吗?

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;
}

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

This will create a coroutine and distribute it to the thread in thread pool.这将创建一个协程并将其分发到线程池中的线程。 Thread pool's Schedular will work in here .线程池的 Schedular 会在这里工作。

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

Now you are in one thread of the thread pool, and the coroutine context will run in this thread, thread pool's schedular will not work here.现在你在线程池的一个线程中,协程上下文将在这个线程中运行,线程池的调度在这里不起作用

Instead of thread pool's schedular, The coroutine's schedular will work here .代替线程池的调度,协程的调度将在这里工作 And this schedular will just run in the same thread .而这个调度将只在同一个线程中运行

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

What is the operator &&?运算符&&是什么?

It create 3 coroutine in the caller's executor, run all of them and wait them for finish.它在调用者的执行程序中创建 3 个协程,运行所有协程并等待它们完成。

So The coroutine function one , two , three will run in the same executor as run_all .所以协程 function onetwothree将在与run_all相同的执行器中运行。

In coroutine function, you can get the executor by co_await asio::this_coro::executor .在协程 function 中,可以通过co_await asio::this_coro::executor

That is why they are run in the same thread.这就是为什么它们在同一个线程中运行。

Remember, the coroutine function shouldn't be blocked.请记住,协程 function 不应被阻塞。 The coroutine's schedular is run in user's code, not in OS.协程的调度在用户代码中运行,而不是在操作系统中。 the function std::this_thread::sleep_for will just block the thread, and coroutine's schedular can't do anything . function std::this_thread::sleep_for只会阻塞线程,协程的调度程序无法执行任何操作。

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

How to let coroutine schedule know that you what to yield for a second?如何让协程调度知道你要让出什么? You need call an async function.您需要调用异步 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;
}

you can see the result here.你可以在这里看到结果。 link关联

in the code co_await clk.async_wait_for(1s) .在代码co_await clk.async_wait_for(1s)中。 you notify the schedular that you want to yield for 1s.你通知你想要 1s 的时间表。 That it is worked.它是有效的。

Remember, if you call an function without co_await or co_return, it must be an blocked call.请记住,如果您在没有 co_await 或 co_return 的情况下调用 function,则它一定是阻塞调用。

ASIO supply many async I/O function, such as timer, file, socket, ssl, pipe, channel, serial ports and signals. ASIO 提供许多异步 I/O function,如定时器、文件、套接字、ssl、pipe、通道、串口和信号。 Those function have an async prefix.那些 function 有一个异步前缀。

By the way, if you want to define your own async call, see this sample:顺便说一句,如果您想定义自己的异步调用,请参阅此示例:

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.
   }
}

another example for heavy calculate:重计算的另一个例子:

//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);
}

It's dirty for author, but beautiful for user.这对作者来说很脏,但对用户来说很漂亮。

暂无
暂无

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

相关问题 在多个线程上发布任务时 boost::asio::thread_pool 线程安全吗? - Is boost::asio::thread_pool thread safe when posting tasks on multiple threads? C++线程池使用boost::asio::thread_pool,为什么我不能重用我的线程? - C++ thread pool using boost::asio::thread_pool, why can't I reuse my threads? 在 lambda 函数中捕获 boost::asio::thread_pool - Capturing boost::asio::thread_pool in lambda function asio :: thread_pool甚至在调用构造函数之前就失败了 - asio::thread_pool fails before constructor is even called asio::io_context 和 asio::thread_pool 有什么区别? - What's the difference between asio::io_context and asio::thread_pool? 如何处理:如果 boost::asio::post 无休止地重复,当 boost::asio::thread_pool 析构函数被触发时? - How to deal: if boost::asio::post is endlessly repeated, when boost::asio::thread_pool destructor is triggered? 我的 boost::asio::thread_pool 中的线程 ID 始终相同 - Thread-ID is always the same in my boost::asio::thread_pool 可以使用 boost::asio::thread_pool 而不是将 boost::asio::io_context 与 boost::thread::thread_group 结合使用吗? - Can boost::asio::thread_pool be used instead of combining boost::asio::io_context with a boost::thread::thread_group? 等到发布到 boost::asio::thread_pool 的作业(与所有作业完全相反)完成? - Wait until A job (as starkly opposed to ALL jobs) posted to boost::asio::thread_pool completes? Helgrind 在简单的 boost::asio::thread_pool 程序中报告同步错误 - Helgrind reports synchronization errors in simple boost::asio::thread_pool program
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM