boost::asio::bind_executor 不在链中执行

2024-04-10

以下示例在没有断言的情况下完成:

#include <cassert>
#include <functional>
#include <future>
#include <thread>
#include <boost/asio.hpp>

class example1
{
public:
    typedef boost::asio::io_context io_context;
    typedef boost::asio::io_context::executor_type executor_type;
    typedef boost::asio::strand<executor_type> strand;
    typedef boost::asio::executor_work_guard<executor_type> work_guard;
    typedef std::function<void()> handler;

    example1()
      : work_(boost::asio::make_work_guard(context_)),
        thread_([this]() { context_.run(); }),
        strand1_(context_.get_executor()),
        strand2_(context_.get_executor())
    {

    }

    ~example1()
    {
        assert(result_.get_future().get());
        work_.reset();
        thread_.join();
    }

    void invoke()
    {
        handler handle = boost::asio::bind_executor(strand2_,
            std::bind(&example1::strand2_handler, this));

        boost::asio::post(strand1_,
            std::bind(&example1::strand1_handler, this, handle));
    }

    void strand1_handler(handler handle)
    {
        assert(strand1_.running_in_this_thread());
        handle();
    }

    void strand2_handler()
    {
        assert(strand1_.running_in_this_thread());
        ////assert(strand2_.running_in_this_thread());
        result_.set_value(true);
    }

private:
    io_context context_;
    work_guard work_;
    std::thread thread_;
    strand strand1_;
    strand strand2_;
    std::promise<bool> result_;
};

int main()
{
    example1 test{};
    test.invoke();
}

然而,我的期望是注释掉的断言应该成功,而不是直接在其上方的断言。根据strand::running_in_this_thread()处理程序handle已在调用者的链中调用,而不是提供给bind_executor.

我可以使用中间方法解决这个问题,如下所示。

class example2
{
public:
    typedef boost::asio::io_context io_context;
    typedef boost::asio::io_context::executor_type executor_type;
    typedef boost::asio::strand<executor_type> strand;
    typedef boost::asio::executor_work_guard<executor_type> work_guard;
    typedef std::function<void()> handler;

    example2()
      : work_(boost::asio::make_work_guard(context_)),
        thread_([this]() { context_.run(); }),
        strand1_(context_.get_executor()),
        strand2_(context_.get_executor())
    {

    }

    ~example2()
    {
        assert(result_.get_future().get());
        work_.reset();
        thread_.join();
    }

    void invoke()
    {
        handler handle =
            std::bind(&example2::do_strand2_handler, this);

        boost::asio::post(strand1_,
            std::bind(&example2::strand1_handler, this, handle));
    }

    void strand1_handler(handler handle)
    {
        assert(strand1_.running_in_this_thread());
        handle();
    }

    // Do the job of bind_executor.
    void do_strand2_handler()
    {
        boost::asio::post(strand2_,
            std::bind(&example2::strand2_handler, this));
    }

    void strand2_handler()
    {
        ////assert(strand1_.running_in_this_thread());
        assert(strand2_.running_in_this_thread());
        result_.set_value(true);
    }

private:
    io_context context_;
    work_guard work_;
    std::thread thread_;
    strand strand1_;
    strand strand2_;
    std::promise<bool> result_;
};

int main()
{
    example2 test2{};
    test2.invoke();
}

但避免这种情况可能是目的bind_executor。这是一个提升错误还是我错过了什么?我尝试通过 boost::asio 源跟踪此操作,但无济于事。

Update

感谢@sehe 提供的大量帮助。上述问题可以通过多种方式解决,例如:

class example3
{
public:
    typedef boost::asio::io_context io_context;
    typedef boost::asio::io_context::executor_type executor_type;
    typedef boost::asio::strand<executor_type> strand;
    typedef boost::asio::executor_work_guard<executor_type> work_guard;
    typedef boost::asio::executor_binder<std::function<void()>,
        boost::asio::any_io_executor> handler;

    example3()
      : work_(boost::asio::make_work_guard(context_)),
        thread_([this]() { context_.run(); }),
        strand1_(context_.get_executor()),
        strand2_(context_.get_executor())
    {
    }

    ~example3()
    {
        assert(result_.get_future().get());
        work_.reset();
        thread_.join();
    }

    void invoke()
    {
        auto handle = boost::asio::bind_executor(strand2_,
            std::bind(&example3::strand2_handler, this));

        boost::asio::post(strand1_,
            std::bind(&example3::strand1_handler, this, handle));
    }

    void strand1_handler(handler handle)
    {
        assert(strand1_.running_in_this_thread());
        boost::asio::dispatch(handle);
    }

    void strand2_handler()
    {
        assert(strand2_.running_in_this_thread());
        result_.set_value(true);
    }

private:
    io_context context_;
    work_guard work_;
    std::thread thread_;
    strand strand1_;
    strand strand2_;
    std::promise<bool> result_;
};

int main
{
    example3 test3{};
    test3.invoke();
}

是的,你确实错过了一些东西。实际上有两件事。

类型擦除

绑定执行器不会修改函数,而是修改其类型。

但是,通过使用擦除可调用的类型std::function<>你隐藏了绑定的执行者。您可以轻松确定这一点:

erased_handler handle = bind_executor(s2, s2_handler);
assert(asio::get_associated_executor(handle, s1) == s1);

当您保留类型时,问题就消失了:

auto handle = bind_executor(s2, s2_handler);
assert(asio::get_associated_executor(handle, s1) == s2);

派遣(以前handler_invoke)

调用handle正如您所发现的,直接根据 C++ 语言语义调用它。

要要求 Asio 尊重潜在的绑定执行者,您可以使用dispatch (or post):

auto s1_handler = [&](auto chain) {
    assert(s1.running_in_this_thread());
    dispatch(get_associated_executor(chain, s1), chain);
};

事实上,如果你是sure that chain将有一个关联的执行程序,您可以接受默认后备(这是系统执行程序):

auto s1_handler = [&](auto chain) {
    assert(s1.running_in_this_thread());
    dispatch(chain);
};

把它们放在一起

展示简化、扩展测试仪的智慧:

住在科里鲁 http://coliru.stacked-crooked.com/a/5eae9e6029794593

#include <boost/asio.hpp>
#include <functional>
#include <iostream>

namespace asio = boost::asio;

int main() {
    asio::thread_pool io(1);

    auto s1 = make_strand(io), s2 = make_strand(io);
    assert(s1 != s2); // implementation defined! strands may hash equal

    auto s1_handler = [&](auto chain) {
        assert(s1.running_in_this_thread());

        // immediate invocation runs on the current strand:
        chain();

        // dispatch *might* invoke directly if already on the right strand
        dispatch(chain);                                     // 1
        dispatch(get_associated_executor(chain, s1), chain); // 2

        // posting never immediately invokes, even if already on the right
        // strand
        post(chain);                                     // 3
        post(get_associated_executor(chain, s1), chain); // 4
    };

    int count_chain_invocations = 0;
    auto s2_handler = [&] {
        if (s2.running_in_this_thread()) {
            count_chain_invocations += 1;
        } else {
            std::cout << "(note: direct C++ call ends up on wrong strand)\n";
        }
    };

    {
        using erased_handler  = std::function<void()>;
        erased_handler handle = bind_executor(s2, s2_handler);
        assert(asio::get_associated_executor(handle, s1) == s1);
    }
    {
        auto handle = bind_executor(s2, s2_handler);
        assert(asio::get_associated_executor(handle, s1) == s2);
    }

    auto handle = bind_executor(s2, s2_handler);
    post(s1, std::bind(s1_handler, handle));

    io.join();

    std::cout << "count_chain_invocations: " << count_chain_invocations << "\n";
}

所有断言都通过,输出符合预期:

(note: direct C++ call ends up on wrong strand)
count_chain_invocations: 4

奖励:如果您需要类型擦除的绑定可调用项怎么办?

无论你做什么,都不要使用std::function。不过,你可以包一个;

template <typename Sig> struct ErasedHandler {
    using executor_type = asio::any_io_executor;
    std::function<Sig> _erased;
    executor_type      _ex;
    executor_type get_executor() const { return _ex; }

    template <typename F>
    explicit ErasedHandler(F&& f)
        : _erased(std::forward<F>(f))
        , _ex(asio::get_associated_executor(f)) {}

    ErasedHandler() = default;

    template <typename... Args>
    decltype(auto) operator()(Args&&... args) const {
        return _erased(std::forward<Args>(args)...);
    }
    template <typename... Args>
    decltype(auto) operator()(Args&&... args) {
        return _erased(std::forward<Args>(args)...);
    }

    explicit operator bool() const { return _erased; }
};

See it 住在科里鲁 http://coliru.stacked-crooked.com/a/3fd0066898eb36d6

在此之前,请注意

  • using any_io_executor类型也会擦除执行器,这可能会损害性能
  • it does not提供良好的后备,只需使用系统执行器来处理未绑定的可调用对象。您可以通过检测它并需要显式构造函数参数等来解决这个问题,但是......
  • 所有这些仍然完全忽略其他处理程序属性,例如关联分配器

我可能会避免一般存储类型擦除的可链接处理程序。您通常可以存储由模板类型参数推导的处理程序的实际类型。

PS:事后思考

您可能期待的是这种行为:

template <typename... Args>
decltype(auto) operator()(Args&&... args) const {
    // CAUTION: NOT WHAT YOU WANT
    boost::asio::dispatch(_ex,
                          std::bind(_erased, std::forward<Args>(args)...));
}
template <typename... Args>
decltype(auto) operator()(Args&&... args) {
    // CAUTION: NOT WHAT YOU WANT
    boost::asio::dispatch(_ex,
                          std::bind(_erased, std::forward<Args>(args)...));
}

看到那个住在科里鲁 http://coliru.stacked-crooked.com/a/3040d582a09f8149

在此方案下,即使是直接的 C++ 调用也会“做正确的事情”。

看起来不错。直到你想到为止。

问题是处理程序不能以这种方式反弹。更具体地说,如果您有一个与“自由线程”执行器关联的处理程序,那么bind_executor(strand, f)不会有任何影响(除了减慢你的程序),因为f无论如何,都会令人讨厌地分派给另一个执行者。

所以不要这样做:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost::asio::bind_executor 不在链中执行 的相关文章

随机推荐

  • 整个应用程序的异常处理

    我对 iPhone 中的异常处理有一些疑问 他们是这样的 假设我有一连串被依次调用的方法 即方法 A 调用方法 B 方法 B 又调用方法 C 方法 C 调用方法 D 这是放置我的 try catch 块的最佳位置 是方法 A B C D 还
  • 用拼凑的方式组合和合并 ggplot2 中的图例

    我想结合两个或多个情节来融合他们的传奇 例如 我可以创建一些数据和两个场景 如下所示 packages library ggplot2 library patchwork first plot set seed 07042020 x lt
  • 我如何知道 Perl 正则表达式的哪一部分与字符串匹配?

    我想搜索文件的行以查看其中是否有任何行与一组正则表达式中的一个匹配 像这样的东西 my regs qr a qr b qr c foreach my line
  • jQuery 对话框回发但 UpdatePanel 未更新

    我想从代码隐藏中显示 jQuery UI 对话框 并且需要在回发后刷新它 该对话框是用于过滤和查找数据的控件 因此 用户从 DropDownLists 中进行选择并在 TextBoxes 中输入文本 单击 Apply Button 发生异步
  • 最佳数据库变更控制方法

    作为数据库架构师 开发人员和顾问 有很多问题可以回答 其中之一 虽然我最近被问到 但仍然无法很好地回答 那就是 保持数据库变更记录 组织并能够在单开发人员或多开发人员环境中有效推出的最佳方法或技术之一是什么 这可能涉及存储过程和其他对象脚本
  • Entity Framework 4 Code-First 多对多插入

    我在数据库层使用代码优先模式 我有两个 POCO 课程 public class Order Key public int OrderId get set public virtual ICollection
  • “float”对象没有属性“__getitem__”Python错误

    当我运行代码时 import numpy as np from scipy integrate import odeint import matplotlib pyplot as plt Initial conditions def f f
  • ChrW(e.KeyCode) 在 C# 中的等价物是什么?

    在VB NET 2008中 我使用了以下语句 MyKeyChr ChrW e KeyCode 现在我想将上面的语句转换成C 有任何想法吗 快速而肮脏的相当于ChrW在 C 中只是将值转换为char char MyKeyChr char e
  • WTForms:在编写自定义验证时传递额外参数

    写作时wtforms 的自定义验证 http wtforms simplecodes com docs 0 6 validators html 是否可以传递额外的参数 如请求 For e g class MyForm Form name T
  • 从 2 元组列表生成最大数量的 3 元组

    我有一个 2 元组列表 并希望从该列表中生成尽可能多的 3 元组 例子 usr bin python import itertools a list itertools combinations 1 2 3 4 5 6 7 8 9 2 i
  • 如何使用 RSpec 测试获取目录中的文件列表?

    我对 RSpec 的世界还很陌生 我正在编写一个 RubyGem 它处理指定目录和任何子目录中的文件列表 具体来说 它将使用Find find并将文件附加到数组以供以后输出 我想编写一个规范来测试这种行为 但真的不知道从哪里开始伪造文件目录
  • 处理大量输入参数、大量输出

    我需要进行一个复杂的计算 就我而言 创建一个计算器类 使用策略模式抽象 似乎是最自然的 为了执行计算 该类需要接受大约 20 个输入 其中一些是可选的 其中一些可能在将来发生变化等 计算 调用方法时 需要输出大约20个不同的变量 有多种方法
  • javascript apply 和 call 方法并链接在一起

    在本文中js日志函数 https gist github com bgrins 5108712 signup true 有一个说法 Function prototype apply call console log 控制台 参数 我对这个说
  • 创建垂直分隔符 Jetpack Compose

    如何使用 Jetpack Compose 创建垂直分隔线 我尝试使用 Spacer 和 Box 来做到这一点 但它根本不显示 这是我尝试过的 Box modifier Modifier fillMaxHeight width 2 dp ba
  • R:使用 marrangeGrob 在空白首页制作 pdf 结果

    我正在制作一些每页上有多个图形的 pdf 文件 并且当我使用 gridextra 包中的 marrangeGrob 来制作这些图形时 第一页始终是空白的 如何使绘图从第一页开始 这是一些示例代码 library gridextra libr
  • IPHONE:ABPeoplePickerNavigationController 隐藏导航栏

    您好 我有一个 ABPeoplePickerNavigationController 在创建时设置其 导航栏隐藏 peoplePickerController navigationBar hidden YES 这工作得很好 唯一的问题是当用
  • Youtube API 和跟踪嵌入视频

    目前的问题是在一个页面上嵌入多个 Youtube 视频 但现在 Youtube 分析无法正常工作 以前我在 Drupal 页面上有很多嵌入视频 但该网站对 SEO 不友好 页面速度很慢 许多嵌入的 Youtube 视频使用 js 和 css
  • 以编程方式调用委托中的故事板

    我正在尝试以编程方式调用我的故事板 我的故事板由以下内容组成 导航控制器 gt MainMenuView gt DetailsView MainMenu 标识符被放置在 MainMenuView 中 我遇到的问题是屏幕显示空白 我需要做什么
  • Devise Omniauth - 设置和定义策略

    我尝试问这个问题 但没有找到任何帮助 http stackoverflow com questions 33493369 rails devise omniauth problems with setup 我放弃了解决问题的尝试 并制作了一
  • boost::asio::bind_executor 不在链中执行

    以下示例在没有断言的情况下完成 include