Boost Asio:C++20 协程中的执行器

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

在尝试

boost::asio::awaitable
Executor
时,我不断观察到一些相当令人困惑的行为,我想更好地理解这些行为

准备工作

请看以下节目:

int main(int argc, char const* args[])
{
    boost::asio::io_context ioc;
    auto spawn_strand = boost::asio::make_strand(ioc);

    boost::asio::co_spawn(spawn_strand,
        [&]() -> boost::asio::awaitable<void>
        {
            auto switch_strand = boost::asio::make_strand(ioc);
            co_await boost::asio::post(switch_strand, // (*)
                boost::asio::bind_executor(switch_strand, boost::asio::use_awaitable));

            boost::asio::post(spawn_strand, [](){
                std::cout << "calling handler\n";
            });

            std::this_thread::sleep_for(std::chrono::seconds(3));
            std::cout << "waking up\n";
        },
        boost::asio::detached);

    std::jthread threads[3]; // provide enough threads to serve strands in parallel
    for (auto& thread : threads)
        thread = std::jthread{ [&]() { ioc.run(); }};
}

正如预期的那样,该程序输出:

calling handler
waking up

具体来说,

"calling handler"
"waking up"
之前打印,因为在
(*)
行之后,协程不再在
spawn_strand
上运行。因此,后者将不会
sleep_for
阻止,并且可以立即运行处理程序。

到目前为止一切顺利,现在让我们揭示一些令人困惑的行为......

观察结果

  1. 事实证明,

    assert
    ing
    spawn_strand == co_await boost::asio::this_coro::executor
    after
    (*)
    确实not失败。我们假设此时执行切换到了
    switch_strand
    ,实际上只是证实了这一事实。因此,此时我希望
    co_await boost::asio::this_coro::executor
    比较等于
    switch_strand
    而不是
    spawn_strand

  2. 如果在

    (*)
    行中,我们将传递给
    post
    的链替换为
    spawn_strand
    ,则输出将更改为:

    waking up
    calling handler
    

    我已经阅读了有关提供给 postbind_executor 的执行器的其他答案(例如

    this
    this
    ),并且通常他们建议前者仅作为后备,以防处理程序不关联的处理程序。这与观察到的输出不匹配。

  3. 现在,让我们对前一段进行更改并添加

    boost::asio::post(switch_strand, [](){
        std::cout << "calling handler\n";
    });
    

    (*)
    行之后。请注意,这次我们使用
    switch_strand
    而不是
    spawn_strand
    。我们将得到以下输出

    waking up
    calling handler
    calling handler
    

    这表明两个处理程序(在

    switch_strand
    上以及
    spawn_strand
    上)都被单个
    sleep_for
    阻止。最终,看起来在
    (*)
    行之后,协程同时在 both 线上运行,这非常令人恼火。

  4. 要点 2. 和 3. 同样适用,我们不是替换传递到

    post
    的支架,而是用
    bind_handler
    替换传递到
    spawn_strand
    的股线。

问题

如何解释这些奇怪的观察结果?在我看来,除了执行器调用处理程序的通常功能之外,

boost::asio::awaitable
还与一个在执行过程中永久存在的附加执行器(即提供给
co_spawn
并可通过
this_coro::executor
访问的执行器)相关联。协程的。然而,我还没有在任何地方找到任何解释;既不是 Boost.Asio C++20 Coroutines Support 的文档,也不是此处相关问题的答案。所以我不相信事情实际上是这样运作的。

c++ boost boost-asio executor c++-coroutine
1个回答
0
投票
    断言
  1. assert(spawn_strand == co_await asio::this_coro::executor);

    是同义反复。你确实在链上生成了科罗,所以它

    是科罗链。您可能想要检查的内容: assert(spawn_strand.running_in_this_thread());

    将会

    (*)线之后失败

    
    

  2. 该行为确实与链接的答案相匹配。关键是显式执行程序参数是
  3. 完成标记

    后备 未绑定执行者时使用。当你裸体发帖时 lambda,就是这样。

    链接的答案主要涉及以下事实:

    asio::use_awaitable

    确实
    将coro的执行器作为其关联的执行器,这意味着显式执行器将不会用于完成,除非被例如覆盖。 bind_executor
    
    

  4. 我想前面的应该已经澄清了这一点。
  5. 最终看起来像是在 (*) 行之后,协程同时在两条链上运行,这非常令人恼火。

    好消息:您可以处于多种状态(并且有充分的理由,例如,如果您需要同步访问多个资源)。

    更好地认识到任何协程根据定义都是它自己的逻辑链。事实上,

    stackful coroutines

    明确地将它们的执行器包装在一个链中。 C++20 协程可能不必这样做,但语言规范指定以保证 coro 主体顺序执行的方式恢复。

  6. (我认为这里没有问题)
© www.soinside.com 2019 - 2024. All rights reserved.