C# Parallel.ForEach最终一次执行一个任务

C# Parallel.ForEach最终一次执行一个任务,c#,multithreading,parallel-processing,C#,Multithreading,Parallel Processing,我有一个要使用parallel.ForEach并行执行的任务列表。它一开始可以并行运行4个任务,但最后一次只能运行一个任务。 以下是并行任务的时间计数: 1234434。。。4443311 最大平行度设置为4。在执行结束时,一次只执行一个任务,所有执行都在同一个线程上运行。我的问题是,为什么我最终要一次完成一项任务?我怎样才能避免这种情况 代码如下: var threadCount = 4; ThreadPool.SetMinThreads(threadCount, threadCount);

我有一个要使用parallel.ForEach并行执行的任务列表。它一开始可以并行运行4个任务,但最后一次只能运行一个任务。 以下是并行任务的时间计数:

1234434。。。4443311

最大平行度设置为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);
    });
此输出显示最后只有1个任务正在运行,而剩下6个任务。限制为4个并行任务,这没有任何意义。当6个任务仍然可用时,我希望看到4个任务并行运行


我应该以不同的方式使用Parallel.ForEach,还是它是一个bug/功能

在查看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在某些情况下效率非常低,使用时应谨慎。

RunOne中的代码是什么?RunOne调用外部进程并等待它完成。您显示的代码100%健壮,工作正常。您尚未显示的代码-RunOne…-这可能是他的错;你能展示一下那个方法吗?+1。问题可能在于如何计算并行执行的数量;至少在RunOne中向我们展示这部分代码。寻求调试帮助的问题为什么这段代码不起作用?必须包括所需的行为、特定的问题或错误以及在问题本身中重现这些问题所需的最短代码。没有明确问题陈述的问题对其他读者来说是没有用处的。我发现,它不是将元素一个接一个地分布到不同的线程,而是将任务列表分割成块,然后将任务列表提供给每个线程,其中有一个Parallel.ForEach重载,它使用您提供的分区器,您可以对线程进行分区无论您想要什么样的任务。例如,对于长时间运行的任务,这是一种非常低效的方法。还有Task.longrung hint Task,longrung hint与Parallel.ForEach无关。我想分区器可以工作,但你可能是世界上两个知道它的人之一:我也不理解默认分区实现背后的逻辑。对泛型方法的假设太多了。我知道不是,但如果你要使用Task.Factory.StartNew和一个长期运行的同步方法,你最好给它一个正确的提示。这是有道理的,尽管如果应用程序没有更多的线程,那么我认为这不会有什么不同。
        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());