C++ lambda回调触发事件

2024-03-19

我一直在尝试了解 C++ 中的回调功能。我想要实现的目标如下:

我有两个对象,每个对象都有自己的线程。一个物体A有一个指向第二个对象的指针B。参见示例:

class A
{
  public:
   // ...
  private:
   std::unique_ptr<B> b;
};

class B
{
  public:
   void add_message(MessageType msg);
   // ...
};

我想要实现的目标是拥有目标A使用指针添加消息B然后继续做其他事情,但有一个回调或处理程序或在什么时候触发的东西B已回复该消息。B对消息进行一些处理,并可能将其传递给其他对象以在其自己的线程上进行处理,但最终会得到回复。那么我怎么知道什么时候B有回复我的消息,例如:

// In class A
MessageType m();
b->add_message(m)
// A's thread continues doing other stuff
...
// some notification that b has a reply?

我知道我可能必须使用 std::function 来进行我想使用的回调,但我无法通过查看大量示例来了解到底如何执行此操作。感谢任何帮助,并请注意,我已经看过很多示例,但无法将其与我想要实现的目标或不理解的目标联系起来......


线程是执行序列。它们的行为大致类似于线性 C++ 程序,嵌入在内存模型中,使它们能够进行通信并注意到由其他执行线程引起的状态变化。

如果没有线程的配合,对线程的回调无法接管执行序列。您想要通知的线程必须显式检查消息是否已到达并处理它。


有两种常见的方法来处理消息响应。

第一个是std::future喜欢的方法。在其中,调用者获得某种令牌,该令牌代表将来可能或将会产生的答案。

第二种是再次使用消息传递。您向 B 发送一条消息,请求回复。 B 将包含响应的消息发送回 A。与 B 接收消息的方式相同,A 也接收回消息。该消息可能包含某种“返回目标”,以帮助 A 将其链接到原始消息。

在基于消息的系统中,通常有一个“事件循环”。您拥有一个反复返回“事件循环”的线程,而不是大型线性程序。它在那里检查队列中是否有消息,如果没有则等待一些消息。

在这样的系统下,任务必须被分解为小块,以便您经常检查事件循环以做出响应。

实现此目的的一种方法是使用协程,这是一种不拥有自己的执行器的执行状态(就像线程一样,它同时拥有两者)。协程定期放弃优先级并“保存其状态供以后使用”。


未来的解决方案通常是最简单的,但它依赖于 A 定期检查响应。

首先,一个threaded_queue<T>,它允许任意数量的生产者和消费者将东西传递到队列中并从前面吃掉它们:

template<class T>
struct threaded_queue {
  using lock = std::unique_lock<std::mutex>;
  void push_back( T t ) {
    {
      lock l(m);
      data.push_back(std::move(t));
    }
    cv.notify_one();
  }
  boost::optional<T> pop_front() {
    lock l(m);
    cv.wait(l, [this]{ return abort || !data.empty(); } );
    if (abort) return {};
    auto r = std::move(data.back());
    data.pop_back();
    return std::move(r);
  }
  void terminate() {
    {
      lock l(m);
      abort = true;
      data.clear();
    }
    cv.notify_all();
  }
  ~threaded_queue()
  {
    terminate();
  }
private:
  std::mutex m;
  std::deque<T> data;
  std::condition_variable cv;
  bool abort = false;
};

现在,我们希望将任务传递到这样的队列中,并让传递任务的人返回结果。以下是上述内容在打包任务中的用法:

template<class...Args>
struct threaded_task_queue {
  threaded_task_queue() = default;
  threaded_task_queue( threaded_task_queue&& ) = delete;
  threaded_task_queue& operator=( threaded_task_queue&& ) = delete;
  ~threaded_task_queue() = default;
  template<class F, class R=std::result_of_t<F&(Args...)>>
  std::future<R> queue_task( F task ) {
    std::packaged_task<R(Args...)> p(std::move(task));
    auto r = p.get_future();
    tasks.push_back( std::move(p) );
    return r;
  }
  void terminate() {
    tasks.terminate();
  }
  std::function<void(Args...)> pop_task() {
    auto task = tasks.pop_front();
    if (!task) return {};
    auto task_ptr = std::make_shared<std::packaged_task<R(Args...)>>(std::move(*task));
    return [task_ptr](Args...args){
      (*task_ptr)(std::forward<Args>(args)...);
    };
  }
private:
  threaded_queue<std::packaged_task<void(Args...)>> tasks;
};

如果我做对了,它的工作原理如下:

  • A 以 lambda 的形式向 B 发送队列任务。这个 lambda 接受一些固定的参数集(由 B 提供),并返回一些值。

  • B 出队列,得到 astd::function这需要参数。它调用它;它返回void在 B 的上下文中。

  • A 被给予future<R>当它对任务进行排队时。它可以查询它以查看它是否完成。

您会注意到,无法“通知”A 事情已完成。这需要不同的解决方案。但如果 A 最终达到了不等待 B 结果就无法前进的地步,那么这个系统就可以工作。

另一方面,如果 A 积累了大量此类消息,有时需要等待许多此类 B 的输入,直到其中任何一个返回数据(或用户执行某些操作),那么您需要比 A 更高级的东西std::future<R>。一般模式——拥有代表未来要交付的计算的令牌——是可靠的。但是您需要对其进行增强,以便与未来计算和消息循环等的多个源良好地配合。

代码未测试。

一种方法threaded_task_queue当您发送消息时:

template<class Signature>
struct message_queue;
template<class R, class...Args>
struct message_queue<R(Args...) :
  threaded_task_queue< std::function<R(Args...)> >
{
  std::future<R> queue_message(Args...args) {
    return this->queue_task(
      [tup = std::make_tuple(std::forward<Args>(args)...)]
      ( std::function<R(Args...)> f ) mutable
      {
        return std::apply( f, std::move(tup) );
      }
    );
  }
  bool consume_message( std::function<R(Args...)> f )
  {
    auto task = pop_task();
    if (!task) return false;
    task( std::move(f) );
    return true;
  }
};

在提供商方面,您提供Args...,而在消费者方面你消费Args...并返回R,在提供者方面你有一个future<R>消费者完成后即可得到结果。

这可能比原始的更自然threaded_task_queue我写。

std::apply是 C++17,但 C++11 和 C++14 也有广泛的实现。

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

C++ lambda回调触发事件 的相关文章

  • 我可以使用反射更改 C# 中的私有只读字段吗?

    我想知道 由于很多事情都可以使用反射完成 我可以在构造函数完成执行后更改私有只读字段吗 注 只是好奇 public class Foo private readonly int bar public Foo int num bar num
  • 高级 Win32 图像文件 I/O?

    我想在 Windows C 应用程序中将图像文件读入内存 什么是一个相当简单的解决方案 也许类似于 IOS 提供的UIImage 我希望支持合理数量的文件格式 我需要为图像处理的位图提供一些低级访问权限 我在互联网上阅读了很多内容 看起来
  • Web UI 中的 .Result 出现死锁

    我正在阅读以下主题http blog stephencleary com 2012 07 dont block on async code html http blog stephencleary com 2012 07 dont bloc
  • 如何调试参数化 SQL 查询

    我使用 C 连接到数据库 然后使用 Ad hoc SQL 来获取数据 这个简单的 SQL 查询非常方便调试 因为我可以记录 SQL 查询字符串 如果我使用参数化 SQL 查询命令 有没有办法记录 sql 查询字符串以进行调试 我想就是这样的
  • 在 Windows Phone 上启动 pdf 文件时出现 System.Runtime.InteropServices.COMException

    我正在尝试使用我之前在另一个应用程序上使用过的以下工作代码打开 pdf 文件 但这一次 当流程到达此行时 我收到 System Runtime InteropServices COMException Windows System Laun
  • 浮点提升:stroustrup vs 编译器 - 谁是对的?

    在 Stroustrup 的新书 C 编程语言 第四版 第 10 5 1 节中 他说 在执行算术运算之前 整数提升用于从较短的整数类型创建整数 类似地 浮点提升是用于从浮点数创建双精度数 我用以下代码确认了第一个声明 include
  • 字节到二进制字符串 C# - 显示所有 8 位数字

    我想在文本框中显示一个字节 现在我正在使用 Convert ToString MyVeryOwnByte 2 但是 当字节开头有 0 时 这些 0 就会被删除 例子 MyVeryOwnByte 00001110 Texbox shows g
  • 阅读 Stack Overflow RSS 源

    我正在尝试获取未回答问题的列表the feed https stackoverflow com feeds 但我在阅读时遇到困难 const string RECENT QUESTIONS https stackoverflow com f
  • C++ 在 Vector 中使用不可分配的对象

    我想将对象列表存储在std vector 但对象包含引用且无法分配给 但是 我可以复制构造该对象 我能想到的唯一选择是使用指针来包装对象并在需要分配指针时重新设置指针 但这样做的语法会显着降低可读性 特别是在使用迭代器时 我更喜欢另一种选择
  • Qt中正确的线程方式

    我的图像加载非常耗时 图像很大 并且在加载时也完成了一些操作 我不想阻止应用程序 GUI 我的想法是在另一个线程中加载图像 发出图像已加载的信号 然后用该图像重绘视图 我的做法 void Window loadImage ImageLoad
  • glDrawElements 只绘制半个四边形

    这是我的功能 void Object draw2 if mIsInitialised return Tell OpenGL about our vertex and normal data glEnableClientState GL VE
  • 成员初始值设定项列表中的求值顺序是什么?

    我有一个带有一些参数的构造函数 我假设它们是按照列出的顺序初始化的 但在一种情况下 它们似乎是按相反的顺序初始化的 导致中止 当我反转参数时 程序停止中止 下面是我正在使用的语法的示例 a 之前需要初始化b 在这种情况下 你能保证这个初始化
  • 当格式字符串包含“{”时,String.Format 异常

    我正在使用 VSTS 2008 C Net 2 0 执行以下语句时 String Format 语句抛出 FormatException 有什么想法是错误的吗 这是获取我正在使用的 template html 的位置 我想在 templat
  • 抽象类和接口之间的区别[重复]

    这个问题在这里已经有答案了 可能的重复 接口与基类 https stackoverflow com questions 56867 interface vs base class 我不明白抽象类和接口之间的区别 我什么时候需要使用哪种字体
  • 在 C# 中赋值后如何保留有关对象的信息?

    我一直在问我的想法可能是解决方案 https stackoverflow com questions 35254467 is it possible in c sharp to get the attributes attached to
  • 在可观察项目生成时对其进行处理

    我有一个IObservable它会生成一次性物品 并且在其生命周期内可能会生成无限数量的物品 因此 我想在每次生成新项目时处理最后一个项目 因此Using http reactivex io documentation operators
  • 用 C# 编写的带有点击移动的 WPF 游戏

    我试图将标签网格移动到鼠标的位置 就像冒险游戏中的移动一样 理想情况下 我会在途中删除并重新绘制它们 但是 现在我只想弄清楚如何将 int 转换为厚度或 pointtoscreen 到目前为止我有 player XMove int Mous
  • 改进C++逐行读取文件的能力?

    我正在解析大约 500GB 的日志文件 我的 C 版本需要 3 5 分钟 我的 Go 版本需要 1 2 分钟 我正在使用 C 的流来流式传输文件的每一行以进行解析 include
  • 多个同名内存数据库

    关系到这个答案 https stackoverflow com a 48446491 596758 我试图通过设置让多个上下文工作UseInMemoryDatabase以同名 下面的测试失败 第二个上下文为空 我还需要做什么才能在内存数据库
  • 如何使用 g++ 在 c++ 20 中使用模块?

    我读了这个链接https gcc gnu org wiki cxx modules https gcc gnu org wiki cxx modules并尝试从该网站复制以下示例 我已经知道这个编译器部分支持模块系统 注 我用的是windo

随机推荐

  • .htaccess 当某些文件不存在时重定向到 404 页面

    我使用自己的 MVC 系统已经有一段时间了 它运行得很好 我在我的 htaccess 中使用它 RewriteEngine On RewriteCond REQUEST FILENAME d RewriteCond REQUEST FILE
  • 在 unix 中操作字符串来创建目录

    我想将一个单词作为输入 并在 unix shell 中使用该单词的字母创建连续的目录 我尝试了 sed awk 和 Fold 命令 但没有获得任何有用的结果 有什么建议吗 例如 如果输入是hello 它应该创建h e l l o目录就像一个
  • 防止 tomcat 在部署时启动应用程序

    有什么方法可以告诉Tomcat不要自动启动我想要部署的应用程序吗 我想手动执行此操作 In CATALINA HOME conf server xml
  • 使用插值时添加换行符

    我有一些用于警报消息框的 html 使用插值 我可以重复使用相同的 html 来显示多个不同的警报消息 它看起来像这样 p myAlertMessage p 现在我想显示一条包含换行符的较长警报消息 但是 我似乎无法以任何会引入换行符的方式
  • 有没有一种简单的方法可以从 Kotlin 中的对象列表中获取数组?

    我希望能得到一个name列表中的数组MSetting 目前我用代码A来做 但是太复杂了 有没有简单的方法呢 Code A fun getName aList List
  • 如何安全地回显文字字符串“%I”?

    我怎样才能回显字符串 I以安全的方式 独立于是否echo命令行被放置在for I loop 例如 在命令提示符中 cmd gt gt gt rem echo is outside of for scope so this works gt
  • 为什么将模型上传到 HuggingFace 存储库的速度如此之慢?

    我有一个问题 我正在尝试将模型推送到 HuggingFace 存储库 问题是它说上传过去 16 小时 而这只是 pytorch model bin 文件 大约 850MB 我用的是LFS 我尝试手动将文件添加到存储库 这需要很长时间 我不愿
  • 在终端中根据括号或圆括号(仅限顶级)分割文本文件

    我有几个文本文件 utf 8 我想在 shell 脚本中处理它们 它们的格式并不完全相同 但如果我只能将它们分解成可食用的块 我就可以处理它 这可以用 C 或 python 编程 但我不喜欢 编辑 我用 C 编写了一个解决方案 看我自己的答
  • getCanonicalName 时抛出 ClassCircularityError

    异常堆栈 Exception in thread main java lang ClassCircularityError plugins agents Agent at java lang Class getDeclaringClass
  • 自定义工具快捷方式 Visual Studio

    有没有办法在 Visual Studio 中创建自定义工具的快捷方式 目前 我必须导航到解决方案中的工具 右键单击并运行自定义工具 如果我可以绑定快捷键或从工具栏运行该工具 那就太好了 有谁知道有什么方法可以做到这一点 我想你正在寻找这个
  • 错误消息是 (#12) bio 字段在 v2.8 及更高版本中已弃用

    我使用了 spring social facebook 的 2 0 3 RELEASE 版本和 Facebook 应用程序 api v2 8 我调用 Facebook 登录但返回了此消息 12 bio 字段在 v2 8 及更高版本中已弃用
  • Symfony / PHP - 存储单个值的最佳方式

    假设我有一个在 Symfony 3 2 Doctrine 上运行的应用程序 问题 存储会随着某些操作而改变的单个值的最佳方法是什么 第一个想法 我认为创建一个具有单个属性的实体将创建一个具有单个列的表 该列将具有单个值 更改某些操作 这不是
  • 用于拒绝请求的 NetflixOSS Zuul 过滤器

    我正在尝试在简单的 spring cloud Netflix Api 网关 反向代理 中使用 ZuulFilter 以便对自定义身份验证提供程序进行请求身份验证 通过 Rest 调用 过滤器应使用 401 拒绝未经授权的请求 并且不要将这些
  • 使用 JDBC 批量删除和插入

    我想知道是否可以使用批处理来执行参数化 DELETE 和 INSERT 语句 我知道如何插入多行 但是 我首先想做一个 DELETE 语句 这需要不同的参数 以下是我插入多个语句的方法 String query INSERT INTO TA
  • 如何将 Wicket 的 DownloadLink 与动态生成的文件一起使用?

    DownloadLink https ci apache org projects wicket apidocs 1 4 x org apache wicket markup html link DownloadLink html创建用于下
  • 类与二维数组

    在 PHP 中 二维数组和类哪个更好用 我举了一个例子来说明我的意思 Using a class class someClass public name public height public weight function constr
  • struct {0} 和 memset 0 有什么区别[重复]

    这个问题在这里已经有答案了 假设我们有这样的结构 struct A int x int y 有什么区别 A a 0 and A a memset a 0 sizeof A 没有任何 最终结果是两者都将结构体成员初始化为0 C99 标准 6
  • Java 有 IndexSet 和 Range 类吗?

    在 Objective C Cocoa 中 我们有NSIndexSet类 它通过保留范围数组来有效地存储一系列唯一索引 例如 集合 1 2 30 57 将存储为范围 1 30 和 57 而不是存储为 32 个数字的数组 这有助于以简单快速的
  • 表格滚动侦听器

    我正在尝试找出滚动表的事件 我可以获取左侧参数 例如使用 scrollTable offset left 但我无法添加其变化的监听器 我想要的就是监视表格左滚动参数的变化并将其应用到另一个 DOM 元素 这不是问题 我的表格的 HTML 示
  • C++ lambda回调触发事件

    我一直在尝试了解 C 中的回调功能 我想要实现的目标如下 我有两个对象 每个对象都有自己的线程 一个物体A有一个指向第二个对象的指针B 参见示例 class A public private std unique ptr b b class