简体   繁体   English

.NET 6的Parallel.ForEachAsync可以嵌套吗?

[英]Can .NET 6's Parallel.ForEachAsync be nested?

I want to do calculations with an outer and an inner loop which I can do in parallel.我想用我可以并行进行的外部和内部循环进行计算。 Furthermore, I want to use the async/await-based programming model .此外,我想使用基于异步/等待的编程 model In the outer loop there is a place where a resource is needed which can only be used by one thread.在外层循环中有一个地方需要一个只能由一个线程使用的资源。

I thought of implementing the loops using ForEachAsync and restrict the access to the resource using SemaphoreSlim :我考虑过使用ForEachAsync实现循环并使用SemaphoreSlim限制对资源的访问:

using System.Linq;
using System.Threading;
using System.Threading.Tasks;
                    
public class Program {
    private static Dictionary<int,IReadOnlyList<int>> resource = new();
    private static SemaphoreSlim semaphore = new(1);
    
    public static async Task Main() {       
        var outerLoopSource = Enumerable.Range(0,10);
        await Parallel.ForEachAsync(outerLoopSource, OuterLoopFunction);
        foreach(var (key, list) in resource)
            Console.WriteLine(key+": "+string.Join(',', list));
    }
                                    
    public static async ValueTask OuterLoopFunction(int i, CancellationToken cancel) {
        // some time consuming calculation ...      
        var key = i%3;
        const int listSize = 10;
        IReadOnlyList<int> list;
        await semaphore.WaitAsync();
        try {
            if(!resource.TryGetValue(key, out list)) {
                var newList = new int[listSize];
                list = newList;
                resource.Add(key, list);
                await Parallel.ForEachAsync(Enumerable.Range(0,listSize), InnerLoopFunction);
                ValueTask InnerLoopFunction(int j, CancellationToken cancel) {
                    // some time consuming calculation ...
                    newList[j] = 42+i;
                    return ValueTask.CompletedTask;
                }
            }
        } finally {
            semaphore.Release();
        }           
        // do something with list
    }   
}

Example in fiddle小提琴中的例子

Can ForEachAsync be used in nested loops like this and is the number of operations in parallel still restricted by System.Environment.ProcessorCount ? ForEachAsync可以在这样的嵌套循环中使用吗?并行操作的数量是否仍受System.Environment.ProcessorCount限制?

Update更新

In the comments people suggested to use dataflow components from the Task Parallel Library .在评论中,人们建议使用任务并行库中的数据流组件 That might be the better approach if I wrote the code from scratch.如果我从头开始编写代码,那可能是更好的方法。 However, in my case there is quite a lot of legacy code doing the calculations and it seems to me I would have to restructure it significantly in order to apply the concept, since I would have to lift what is currently the inner loop on the same level as the outer loop.然而,在我的例子中,有相当多的遗留代码在进行计算,在我看来,为了应用这个概念,我必须对它进行显着的重组,因为我必须解除当前的内部循环水平作为外循环。 Hence I wonder if using annother SemaphoreSlim to restrict the number of parallel executions as described here avoids running to many tasks/threads in parallel without too much performance penalty.因此,我想知道是否按照此处所述使用另一个 SemaphoreSlim 来限制并行执行的数量,从而避免并行运行许多任务/线程而不会造成太大的性能损失。

No, the ParallelOptions.MaxDegreeOfParallelism affects only the degree of parallelism of the configured Parallel.ForEachAsync loop.不, ParallelOptions.MaxDegreeOfParallelism仅影响配置的Parallel.ForEachAsync循环的并行度。 It's not an ambient property that affects all other parallel loops that might be nested inside the outer parallel loop.它不是影响可能嵌套在外部并行循环内的所有其他并行循环的环境属性。 For example if you configure the outer parallel loop with MaxDegreeOfParallelism = 5 , and the inner parallel loop with MaxDegreeOfParallelism = 3 , the delegate of the inner parallel loop might be invoked concurrently 15 times (5 * 3) at any given moment.例如,如果您将外部并行循环配置为MaxDegreeOfParallelism = 5 ,将内部并行循环配置为MaxDegreeOfParallelism = 3 ,则内部并行循环的委托可能在任何给定时刻同时调用 15 次 (5 * 3)。

This assuming that the inner parallel loop is unrestricted.这假设内部并行循环不受限制。 In your example you have enclosed the inner parallel loop in a protected region, by using a SemaphoreSlim(1) .在您的示例中,您已使用SemaphoreSlim(1)将内部并行循环封闭在受保护区域中。 So only one inner parallel loop can be active at any given moment.因此,在任何给定时刻,只有一个内部并行循环可以处于活动状态。 The maximum number of concurrent invocations of the inner loop's delegate is Environment.ProcessorCount (the default MaxDegreeOfParallelism for the Parallel.ForEachAsync API).内部循环委托的最大并发调用数是Environment.ProcessorCountParallel.ForEachAsync API 的默认MaxDegreeOfParallelism )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM