C++ 上下文中的执行程序模式是什么?

Pra*_*tic 7 c++ design-patterns executors

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

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

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

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

Sha*_*ger 6

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

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

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

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