2017-10-09 20 views
-1

我有一列我想要使用Parallel.ForEach并行执行的任务。它可以很好地开始并行运行4个任务,但最终每次只能减少一个任务。 这里是并行任务的时间计数:Parallel.ForEach最后在当时执行一个任务

1 2 3 4 4 3 4 4 ... 4 4 4 3 3 1 1 1 1 1 1 1

并行

最大程度被设置为4 。执行结束时,一次只执行一个任务,所有执行都在同一个线程上运行。我的问题是为什么我最终一次执行这个任务?我怎样才能避免这种情况?

下面是代码:

var threadCount = 4; 
ThreadPool.SetMinThreads(threadCount, threadCount); 
Parallel.ForEach(taskDataList, 
    new ParallelOptions() {MaxDegreeOfParallelism = threadCount}, 
    (x) => { RunOne(x); }); 

RunOne函数启动外部处理并等待其结束。有人怀疑RunOne可能是缺乏并行执行的问题。为了确保不是这种情况,我通过用相同持续时间的睡眠呼叫替换此函数来重新创建情况。 代码如下。这里t是每个任务花费的秒数。 activeCount是当前正在运行的任务的数量,其余是仍然保留在列表中的任务数量。

var t = new List<int>() 
{2,2,2,1,1,1,1,1,1,1, 
1,1,1,1,1,3,1,1,1,1, 
1,1,1,1,1,1,1,1,5,4, 
26,12,11,16,44,4,37,26,13,36}; 
int activeCount = 0; 
int remaining = t.Count; 
Parallel.ForEach(t, new ParallelOptions() {MaxDegreeOfParallelism = 4}, 
    (x) => 
    { 
     Console.WriteLine($"Active={Interlocked.Increment(ref activeCount)}"+ 
      $"Remaining={Interlocked.Decrement(ref remaining)} " + 
      $"Run thread={Thread.CurrentThread.ManagedThreadId}"); 
     Thread.Sleep(x * 1000); //Sleep x seconds 
     Interlocked.Decrement(ref activeCount); 
    }); 

在快结束它产生的输出是这样的:

Active=2 Remaining=7 Run thread=3 
Active=1 Remaining=6 Run thread=3 
Active=1 Remaining=5 Run thread=3 
Active=1 Remaining=4 Run thread=3 
Active=1 Remaining=3 Run thread=3 
Active=1 Remaining=2 Run thread=3 
Active=1 Remaining=1 Run thread=3 
Active=1 Remaining=0 Run thread=3 

此输出显示,在年底的时候6个任务仍然只有1个任务运行。由于4个并行任务的限制,它没有任何意义。当6个任务仍然可用时,我希望看到4个任务并行运行。

我应该不同地使用Parallel.ForEach或者它是一个错误/功能?

+2

'RunOne()'中的代码是什么? –

+0

RunOne调用外部进程并等待它完成。 –

+1

你已经显示的代码是100%健壮的并且正常工作。你没有显示的代码 - “RunOne(...)' - 可能在这里出错;你可以请出示这种方法吗? – Enigmativity

回答

0

在查看了Parallel.ForEach的参考资源之后,我发现不是将元素逐一分配给不同的线程,而是将任务列表分割成块,然后将任务列表提供给每个线程。它是长期运行的任务

 var t = new List<int>() 
      {2,2,2,1,1,1,1,1,1,1, 
      1,1,1,1,1,3,1,1,1,1, 
      1,1,1,1,1,1,1,1,5,4, 
      26,12,11,16,44,4,37,26,13,36}; 
     int activeCount = 0; 
     int remaining = t.Count; 
     var cq = new ConcurrentQueue<int>(t); 
     var tasks = new List<Task>(); 
     for (int i = 0; i < 4; i++) tasks.Add(Task.Factory.StartNew(() => 
     { 
      int x; 
      while (cq.TryDequeue(out x)) 
      { 
       Console.WriteLine($"Active={Interlocked.Increment(ref activeCount)} " + 
        $"Remaining={Interlocked.Decrement(ref remaining)} " + 
        $"Run thread={Thread.CurrentThread.ManagedThreadId}"); 
       Thread.Sleep(x * 1000); //Sleep x seconds 
       Interlocked.Decrement(ref activeCount); 
      } 
     })); 
     Task.WaitAll(tasks.ToArray()); 

我用4个并行任务如在第一代码例如是非常低效的方法。在使用Parallel.ForEach花费211秒时,在这种情况下执行时间为83秒。这只是证明了Parallel.ForEach在某些情况下非常低效,应谨慎使用。

+0

*我发现,不是将元素逐个分发给不同的线程,而是将任务列表分成块和然后给每个线程的任务列表*有​​一个'Parallel.ForEach'的重载使用你提供的分区器,你可以分割任务,但是你想要,例如https://msdn.microsoft.com/en-us/library/dd381768(v=vs.110).aspx#Examples –

+0

*对于长时间运行的任务,这是非常低效的方法*还有'Task.LongRunning'提示https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcreationoptions(v=vs.110).aspx –

+0

任务,LongRunning提示与Parallel.ForEach无关。分区程序会工作我猜,但你可能是世界上知道它的两个人之一:)我也不理解默认分区实现背后的逻辑。通用方法的假设太多了。 –

相关问题