Boost ASIO:什么执行器与默认完成令牌相关联?他们应该在本地进行表演吗?

问题描述 投票:0回答:1

考虑以下示例(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()
    会更快吗?还是无关紧要?
c++ boost-asio c++-coroutine
1个回答
0
投票

默认关联的执行器是协程的。你提取的那个

auto executor = co_await asio::this_coro::executor;

实际上,您可以通过稍微增强的演示来使其更加明显

住在Coliru

#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() 会更快吗?还是无关紧要?

您可以随时测量。我预计携带和解码显式关联的执行程序绑定器会损害性能,但编译器有可能在内联后将其全部优化?

© www.soinside.com 2019 - 2024. All rights reserved.