C++,当每个工作线程必须执行几个不同的任务时,如何为任务实现线程池

C++, How to implement Thread pool for tasks when each worker thread has to do few different tasks

我正在分析视频流。对于每个新图像(帧),我依次执行以下 3 个任务:

  1. 缩小图片
  2. 检测人脸
  3. 追踪图像中最重要的 4 张面孔

为了在 4-cpu 机器上加快速度,我使用了 4 个工作线程。通过以下方式

  1. 主进程获取图像。创建 4 个工作线程,将图像分成 4 个四分之一,每个工作线程重新调整其 1/4 图像像素的大小。主进程等待线程完成并将四分之一组装成最终图像
  2. 主进程创建了 4 个新的人脸检测 worker。我检测到 4 种类型的面孔(男性、女性、婴儿、狗)。每个工作线程负责一种类型。主进程等待工作人员完成并组装结果(所有现有面孔的列表)。
  3. 主进程创建了 4 个新的 worker 用于面部跟踪。 select 编辑了 4 张最重要的面孔,每个工作人员跟踪 1 张面孔。主进程等待完成。

我的实现的问题是我没有线程池。在每个视频帧(大约每秒 30 次)上,主进程启动并杀死 12 个工人(4 个工人 x 3 个不同的任务)。所以很多时间都浪费在了线程管理上。目前我使用 _beginthreadex() 方法为特定任务的工作线程提供午餐

所需的解决方案: 我只想创建一次 4 个工作线程(每个工作线程能够执行所有 3 个不同的任务)。这些工作人员将贯穿整个视频处理过程。在每个视频帧上,主进程会将图像大小调整任务交给工作人员,而不是检测和稍后的跟踪。

一个丑陋的实现 是每个工作线程都是一个大函数,实现了所有 3 个任务。主进程只是告诉每个工作人员要执行哪个任务(工作人员有一个 'switch' 语句 select 请求的任务)。这是一个丑陋的解决方案,因为将来当我在管道中处理 30 个不同的任务而不是 3 个时——工作人员的代码将变得庞大。此外,这个解决方案违反了封装规则,因为它要求所有任务都驻留在同一个函数中+对于每个新任务,我需要更改工作人员的代码

一个干净的实现 将是主进程为每个工作进程提供一个指向函数(要执行哪个任务)和一些参数的指针。因此,我可以轻松地在我的视频处理管道中添加新任务,而无需更改工作人员的代码,因为工作人员的代码是通用的(执行指向函数的指针并等待带有新指针的请求到达) 但是这里的问题是每个任务都有不同数量的参数(不同的函数接口)并且worker不知道如何call/execute给定函数的地址。

在我的案例中,使用线程池的好方法是什么,同时尽可能保持代码通用,并能够将其从 3 个任务扩展到 30 个。

P.s。 - 我的代码可以在任何平台上运行(Android、iOS、linux、windows 服务器、windows phone 等)。所以我更喜欢通用解决方案,而不是 OS 特定或编译器特定的解决方案

你的错误是你太专注于使用函数

一种老式的方法是让基 class Task 具有成员函数 virtual void operator()();。然后,对于任何应该成为任务的任务,您可以创建 Task 的子 class,其中包含 运行 所需的所有相关数据,并提供对 operator() 的适当覆盖。

一种更现代的方法是创建 std::function<void(void)> 的任务实例,这不仅适用于上述方法,而且适用于您实际拥有具有该签名和 lambda 的函数的情况。 (或者因为你正在做多线程,可能需要像 std::packaged_task<void(void)> 这样的东西;我还没有真正研究过它们是如何使用的)

无论哪种方式,一旦您的工作线程获得对任务的引用,它们就会简单地调用 task(); 来执行任务。