[英]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
one
、 two
、 three
将在与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.