![](/img/trans.png)
[英]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.