Design Pattern for multithreaded task overall progress report
I have a library with an IJobMaker
entity that creates a certain amount of IJob
objects to be run on threads of their own managed by the user.
To track each IJob
's progress I implement the observer pattern with an IProgressObserver
within every job. The difficulty arises when I wish to report on OVERALL progress.
The ideal for me would be to have IProgressO开发者_运维技巧verserver.ReportProgress(float jobProgress, float overallProgress
that reports both job and overall progress.IJobMaker
can be aware of each job's portion of the overall work and somehow gather everyone's reports.
Two main questions arise:
Synchronization mechanism? Keeping a mutex inside
IJobMaker
for example could harm performance becauseIProgressOverserver.ReportProgress
gets called a lot and a mutex could incur a context switch and what not. InterlockedIncrement looks like a good option but since there's no such function for floating point, I would be forced to report progress by integer increments. (I'd like to stay away from c++0x features or Boost)Design pattern?
IJob
's progress is reported from within its deepest algorithms. I need every such a report to both communicate with a central entity for overall progress calculation and call theIProgressObserver.ReportProgress
method which resides inIJob
.
A couple of suggestions on the threading front:
- Don't report every tiniest bit of progress. Only report to the main thread once a certain predefined amount of progress has been made, or a certain predefined amount of time has passed, or the sub-job has finished. This could greatly cut down on the amount of synchronization.
- If you implement #1, a mutex might work pretty well.
- If the mutex turns out to be too expensive, you could report progress using an atomic integer variable: simply scale the values from "no progress" to "all done" to
0
...INT_MAX
.
As far as designing the API, it shouldn't be too difficult to come up with something sensible. My general advice would be to not overengineer it.
First of all, it's quite bad practice to use floats in such cases. Use an integer.
There is another suggestion. You can use segmentation - synchronise only few threads by one mutex/atomic (one segment). And then collect total among all segments.
Also, there is good place to start looking around highly parallel algorithms: http://www.1024cores.net/home/lock-free-algorithms
UDPATE There is example of problems with the 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;
}
So, if you have 100 jobs with 1000 steps each, you will have 1.0 result in 98 earlier than you could expect.
精彩评论