多线程任务总体进度报告的设计模式

Design Pattern for multithreaded task overall progress report

本文关键字:报告 设计模式 任务 多线程      更新时间:2023-10-16

我有一个带有IJobMaker实体的库,它创建了一定数量的IJob对象,这些对象将在用户管理的线程上运行。为了跟踪每个IJob的进度,我在每个作业中使用一个IProgressObserver实现观察者模式。当我想报告总体进展时,困难就出现了。

我的理想是有IProgressOverserver.ReportProgress(float jobProgress, float overallProgress报告工作和总体进展。IJobMaker可以知道每个工作在整体工作中的部分,并以某种方式收集每个人的报告。

出现了两个主要问题:

  1. 同步机制?例如,在IJobMaker中保留互斥锁可能会损害性能,因为IProgressOverserver.ReportProgress会被调用很多次,而互斥锁可能会导致上下文切换等等。InterlockedIncrement看起来是一个不错的选择,但由于没有这样的浮点函数,我将被迫以整数增量报告进度。(我想远离c++0x功能或Boost)

  2. 设计模式?IJob的进展是从其最深层的算法中报告的。我需要每一个这样的报告,既与中央实体进行总体进度计算,又调用IProgressObserver.ReportProgress方法,该方法驻留在IJob中。

关于线程的几个建议:

  1. 不要报告每一个微小的进展。只有当某个预定义的进度量已经完成,或者某个预定义的时间量已经过去,或者子作业已经完成时,才向主线程报告。这可以大大减少同步的数量。
  2. 如果你实现了#1,互斥锁可能会工作得很好。
  3. 如果互斥量太昂贵,你可以使用原子整数变量报告进度:简单地将值从"no progress"缩放到"all done"到0INT_MAX

就设计API而言,想出一些合理的东西应该不会太难。我的建议是不要过度设计。

首先,在这种情况下使用浮点数是非常糟糕的做法。使用整数

还有一个建议。你可以使用分段——通过一个互斥锁/原子(一个段)只同步几个线程。然后在所有段中收集总数

此外,还有一个很好的地方可以开始研究高度并行的算法:http://www.1024cores.net/home/lock-free-algorithms

UDPATE 下面是float

的问题示例
#include <iostream>
using namespace std;
int main() {
    float f = 0;
    for(int i=0; i<100000-98; ++i)
    {
        f += 0.00001;
    }
    cout << f << endl;
}

因此,如果您有100个作业,每个作业有1000个步骤,那么您将在98年得到1.0的结果,比您预期的要早。