C#parallel foreach同样完成任务

时间:2013-03-06 15:56:06

标签: c# parallel-processing

我正在使用C#Parallel.ForEach来处理超过数千个数据子集。一套需要5到30分钟来处理,具体取决于套装的大小。在带有选项

的计算机中
ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = Environment.ProcessorCount

我将获得8个并行进程。据我所知,流程在并行任务之间平均分配(例如,第一个任务获得工作号1,9,17等,第二个任务获得2,10,18等);因此,一项任务可以比其他任务更快地完成自己的工作。因为这些数据集花费的时间比其他数据集少。

问题是四个并行任务在24小时内完成工作,但最后一个任务在48小时内完成。有没有机会组织并行性,以便所有并行任务完全平等?这意味着所有并行任务将继续有效,直到完成所有工作?

3 个答案:

答案 0 :(得分:4)

由于作业不相等,您无法在处理器之间拆分作业数,并使它们几乎同时完成。我认为你需要的是8个工作线程,它们可以检索下一个工作。你必须使用函数锁来获得下一份工作。

如果我错了,有人会纠正我,但是在我的脑海中......工作线程可以被赋予这样的功能:

public void ProcessJob()
{
    for (Job myJob = GetNextJob(); myJob != null; myJob = GetNextJob())
    {
        // process job
    }
}

获得下一份工作的功能如下:

private List<Job> jobs;
private int currentJob = 0;

private Job GetNextJob()
{
    lock (jobs)
    {
        Job job = null;
        if (currentJob < jobs.Count)
        {
            job = jobs[currentJob];
            currentJob++;
        }
        return job;
    }
}

答案 1 :(得分:1)

似乎没有现成的解决方案,必须创建它。

我之前的代码是:

var ListOfSets = (from x in Database
           group x by x.SetID into z
           select new { ID = z.Key}).ToList();

ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = Environment.ProcessorCount;

Parallel.ForEach(ListOfSets, po, SingleSet=>
{
     AnalyzeSet(SingleSet.ID);
});

为了在所有CPU之间平等分享工作,我仍然使用Parallel来完成工作,但我使用ForEach而不是For和Matt的想法。新代码是:

Parallel.For(0, Environment.ProcessorCount, i=>
{
    while(ListOfSets.Count() > 0)
    {
        double SetID = 0;
        lock (ListOfSets)
        {
            SetID = ListOfSets[0].ID;
            ListOfSets.RemoveAt(0);
        }
     AnalyzeSet(SetID);
    }
});

所以,谢谢你的建议。

答案 2 :(得分:1)

其他人建议的一个选择是管理您自己的生产者消费者队列。我想请注意,使用BlockingCollection可以轻松完成此非常

BlockingCollection<JobData> queue = new BlockingCollection<JobData>();

//add data to queue; if it can be done quickly, just do it inline.  
//If it's expensive, start a new task/thread just to add items to the queue.
foreach (JobData job in data)
    queue.Add(job);

queue.CompleteAdding();

for (int i = 0; i < Environment.ProcessorCount; i++)
{
    Task.Factory.StartNew(() =>
    {
        foreach (var job in queue.GetConsumingEnumerable())
        {
            ProcessJob(job);
        }
    }, TaskCreationOptions.LongRunning);
}