考虑以下示例(Godbolt):
#include <boost/asio.hpp>
#include <print>
#include <thread>
namespace asio = boost::asio;
inline void out(auto const& msg) {
static const std::hash<std::thread::id> h {};
std::println("T{} {}", h(std::this_thread::get_id()) % 100, msg);
}
asio::awaitable<void> f(auto strand) {
auto executor = co_await asio::this_coro::executor;
auto to_strd = bind_executor(strand, asio::deferred);
auto to_coro = bind_executor(executor, asio::deferred);
out("I'm on main");
co_await dispatch(strand, asio::deferred);
out("Still on main, asio::deferred has an associated executor");
co_await dispatch(to_strd);
out("Now I'm on the strand");
co_await dispatch(asio::deferred);
out("Back on main, using asio::deferred's associated executor");
co_await dispatch(to_strd);
out("Strand again");
co_await dispatch(to_coro);
out("Was this faster than dispatch(asio::deferred)?");
}
int main() {
asio::io_context io;
asio::thread_pool tp(1);
co_spawn(io, f(make_strand(tp)), asio::detached);
out("<--- this is main");
io.run();
tp.join();
}
当
dispatch()
'ing 或 post()
'ing 到 asio 提供的完成标记(例如 deferred
和 use_awaitable
)时,任何提供的执行器都会被忽略。相反,使用 this_coro::executor
(我认为)。
两个问题:
dispatch()
怎么会变成this_coro::executor
?机制是什么?因为它不是 executor_binder
,它是一个空类型。我知道它从协程堆栈中获取 executor
,我们是如何到达那里的?bind_executor()
会更快吗?还是无关紧要?默认关联的执行器是协程的。你提取的那个
auto executor = co_await asio::this_coro::executor;
实际上,您可以通过稍微增强的演示来使其更加明显
#include <boost/asio.hpp>
#include <fmt/core.h>
#include <thread>
namespace asio = boost::asio;
using namespace std::chrono_literals;
static std::atomic_int tid_gen;
thread_local int const tid = tid_gen++;
inline void out(auto const& msg) { fmt::print("T{:x} {}\n", tid, msg); }
asio::awaitable<void> f(auto strand) {
auto original = co_await asio::this_coro::executor;
auto to_strand = bind_executor(strand, asio::deferred);
auto to_orig = bind_executor(original, asio::deferred);
out("------\n");
out("I'm on original");
co_await dispatch(strand, asio::deferred);
out("Still on original, asio::deferred has an associated executor");
co_await dispatch(to_strand);
out("Now I'm on the strand");
co_await dispatch(asio::deferred);
out("Back on original, using asio::deferred's associated executor");
co_await dispatch(to_strand);
out("Strand again");
co_await dispatch(to_orig);
out("Was this faster than dispatch(asio::deferred)?");
}
int main() {
out("<--- this is main");
asio::thread_pool tp1(1);
post(tp1, [] { out("I'm on tp1"); });
asio::thread_pool tp2(1);
post(tp2, [] { out("I'm on tp2"); });
std::this_thread::sleep_for(10ms); // not very important but slightly simplifies output
{
asio::io_context io;
co_spawn(io, f(make_strand(tp1)), asio::detached);
io.run();
}
{
asio::io_context io;
co_spawn(io, f(make_strand(tp2)), asio::detached);
io.run();
}
{ //
co_spawn( //
tp1, //
f(make_strand(tp2)), //
consign(asio::detached, make_work_guard(tp1)) //
);
}
tp1.join();
tp2.join();
}
印刷
T0 <--- this is main
T1 I'm on tp1
T2 I'm on tp2
T0 ------
T0 I'm on original
T0 Still on original, asio::deferred has an associated executor
T1 Now I'm on the strand
T0 Back on original, using asio::deferred's associated executor
T1 Strand again
T0 Was this faster than dispatch(asio::deferred)?
T0 ------
T0 I'm on original
T0 Still on original, asio::deferred has an associated executor
T2 Now I'm on the strand
T0 Back on original, using asio::deferred's associated executor
T2 Strand again
T0 Was this faster than dispatch(asio::deferred)?
T1 ------
T1 I'm on original
T1 Still on original, asio::deferred has an associated executor
T2 Now I'm on the strand
T1 Back on original, using asio::deferred's associated executor
T2 Strand again
T1 Was this faster than dispatch(asio::deferred)?
额外问题:
问.dispatch() 如何以 this_coro::executor 结束?机制是什么?因为它不是 executor_binder,它是一个空类型。我知道它从协程堆栈中获取执行器,我们是如何到达那里的?
它由 Promise-type 的
await_transform
方法处理。您可能应该在 awaitable_frame_base
中找到它
Q. 尽管这可行,但对于延迟性能来说,如果我们要多次执行此操作,那么在顶部的 bind_executor() 会更快吗?还是无关紧要?
您可以随时测量。我预计携带和解码显式关联的执行程序绑定器会损害性能,但编译器有可能在内联后将其全部优化?