【问题标题】:What is the executor pattern in a C++ context?C++ 上下文中的执行者模式是什么?
【发布时间】:2017-02-11 15:45:36
【问题描述】:

asio 的作者 Christopher Kohlhoff 正在为 C++ 中的执行器开发一个库和提案。到目前为止,他的工作包括repodocs。不幸的是,基本原理部分还没有写出来。到目前为止,这些文档给出了一些图书馆所做的例子,但我不觉得我错过了什么。不知何故,这不仅仅是一系列花哨的调用函数。

我在 Google 上找到的所有内容都非常特定于 Java,其中很多都是特定于特定框架的,所以我很难弄清楚这种“执行程序模式”是什么。

在这种情况下,执行者是什么?他们在做什么?什么时候它们会有所帮助的典型例子是什么?执行者之间存在哪些差异?执行者的替代方案是什么?它们如何比较?特别是,似乎与事件循环有很多重叠,其中事件是初始输入事件、执行事件和关闭事件。

在尝试找出新的抽象概念时,我通常会发现理解动机的关键。那么对于执行者来说,我们想要抽象什么,为什么?我们试图使什么通用?如果没有执行者,我们还要做哪些额外的工作?

【问题讨论】:

  • 使用重载调用operator()(... args) 运算符?
  • @πάνταῥεῖ 我不明白你的评论。
  • 嗯,这是表示执行者/函子模式的惯用方式。
  • @πάνταῥεῖ 听起来您正在考虑函数调用运算符的语法。 “执行者”是一种抽象。我想了解可以被视为执行者的对象的一般规则和特征。
  • 我认为这就像这篇论文所说的那样:Parallel Algorithms Need Executors | N4406

标签: c++ design-patterns executors


【解决方案1】:

执行器最基本的好处是将程序并行性的定义与其使用方式分开。 Java 的执行器模型之所以存在,是因为总的来说,当您第一次编写代码时,您实际上并不知道哪种并行模型最适合您的场景。您可能无法从并行性中获得什么好处,并且根本不应该使用线程,您最好为每个内核使用一个长时间运行的专用工作线程,或者根据当前负载动态扩展线程池,在它们之后清理线程'已经空闲了一段时间以减少内存使用、上下文切换等,或者可能只是按需为每个任务启动一个线程,并在任务完成时退出。

这里的关键是,当您第一次编写代码时,几乎不可能知道哪种方法最好。您可能知道并行性可能对您有什么帮助,但在传统线程中,您最终将并行性“配置”(何时以及是否创建线程)与并行性的使用(确定要调用的函数)混合在一起什么论据)。当你像这样混合代码时,对不同选项进行性能测试是一件非常痛苦的事情,因为每个线程启动都是独立的,并且必须单独更新。

执行器模型的主要好处是并行配置在一个位置(创建执行器的位置)完成,并且该位置的用户 executor 不需要知道任何事情。 他们只需将工作提交给 executor,接收一个 future,然后在稍后的某个时刻,从 future 中检索结果(如果需要,阻塞)。如果您想尝试其他配置,请更改定义执行程序的一行并再次运行您的代码。即使您决定需要为代码的不同部分使用不同的并行模型,与手动重写每个站点的线程细节相比,重构以添加第二个执行程序并更改第一个执行程序的一些用户以使用第二个执行程序也很容易;只要执行者的名称(相对)唯一,查找用户并更改他们以使用不同的用户非常容易。执行器既可以简化您的代码(通过避免将线程创建/管理与线程执行的任务混合在一起),又可以简化性能测试。

作为附带的好处,您还可以抽象出将数据传入和传出工作线程的复杂性(submit 方法封装了前者,future 的result 方法封装了后者)。 std::async 为您带来了一些好处,但无法真正控制所涉及的并行性(只是选择是/否/可能选择是否强制线程,强制在当前线程中延迟执行,或者让编译器/库决定,对是否使用线程池没有细粒度的控制,如果是,它的行为方式)。真正的执行器框架为您提供std::async 无法提供的控制,并具有类似的易用性。

【讨论】:

  • 如果我没记错的话,这会映射到.net 的TaskScheduler?或者也许 Task 和 TaskScheduler 如何表示 Task 是一个工作单元的职责分离,而调度程序决定何时/如何/是否将其调度到可以是专用线程、线程池中的某个线程或其他可能的上下文绑定到 UI 框架。
  • @TanveerBadar:是的,这看起来填补了类似的利基市场。 .NET 的方法似乎试图鼓励使用 TaskFactory 来零碎地创建任务,而 Java 接受特定接口 (Callable<T>) 的方法通过构造函数绑定参数并通过接口方法返回结果,但是想法大致相同。由于lambdas 和可变参数模板,C++ 实际上可能更容易使用(不需要TaskFactory 或实现Callable,当您可以直接传递函数引用和参数时),但想法是一样的。
  • 感谢您的回答和进一步的解释。我可以肯定地说我今天学到了一些新东西。
猜你喜欢
  • 2012-03-12
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 2020-09-13
  • 2014-07-24
  • 2023-03-31
相关资源
最近更新 更多