[英]Is boost::asio::thread_pool thread safe when posting tasks on multiple threads?
[英]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;
}
这将创建一个协程并将其分发到线程池中的线程。 线程池的 Schedular 会在这里工作。
co_spawn( pool, run_all(), asio::detached );
现在你在线程池的一个线程中,协程上下文将在这个线程中运行,线程池的调度在这里不起作用。
代替线程池的调度,协程的调度将在这里工作。 而这个调度将只在同一个线程中运行。
asio::awaitable<void> run_all()
{
auto [first, second, third] = co_await( one() && two() && three() );
}
运算符&&是什么?
它在调用者的执行程序中创建 3 个协程,运行所有协程并等待它们完成。
所以协程 function one
、 two
、 three
将在与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.