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

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

本文关键字:任务 线程 几个 实现 工作 C++ 执行      更新时间:2023-10-16

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

  1. 缩小图像大小
  2. 检测人脸
  3. 跟踪图像中最重要的4个面

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

  1. 主进程得到一个图像。创建4个工作线程,将图像拆分为4个四分之一,每个工作线程重新调整其1/4的图像像素大小。主进程等待线程完成并将四分之一装配到最终图像
  2. 主流程为人脸检测创建了4名新员工。我检测到4种类型的脸(雄性、雌性、婴儿、狗)。每个工作线程负责一种类型。主进程等待工人完成并组装结果(所有现有面的列表)
  3. 主流程为人脸跟踪创建了4名新员工。选择4个最重要的面,并且每个工人跟踪1个面。主进程等待完成

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

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

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

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

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

附言-我的代码可以在任何平台上运行(Android、iOS、linux、windows服务器、windows手机等)。因此,我更喜欢通用解决方案,而不是特定于操作系统或编译器的解决方案

您的错误是过于专注于使用函数

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

一种更现代的方法是使任务成为std::function<void(void)>的实例,这不仅适用于上述方法,而且适用于那些实际具有该签名和lambdas的函数的情况。(或者,由于您正在进行多线程处理,可能需要std::packaged_task<void(void)>之类的东西;我还没有真正研究过这些东西是如何使用的)

无论哪种方式,一旦您的工作线程获得了对任务的引用,它们只需调用task();来执行该任务。