【问题标题】:ParallelEnumerable.WithDegreeOfParallelism() not restricting tasks?ParallelEnumerable.WithDegreeOfParallelism() 不限制任务?
【发布时间】:2018-07-07 07:53:39
【问题描述】:

我正在尝试将 AsParallel() 与 async-await 一起使用,以使应用程序并行处理一系列任务,但由于任务启动了具有大量内存使用的外部进程,因此并发程度受到限制(因此希望等待该过程完成,然后再继续进行系列中的下一个项目)。我在函数ParallelEnumerable.WithDegreeOfSeparation 上看到的大多数文献表明,使用它会在任何时候为并发任务设置最大限制,但我自己的测试似乎表明它完全跳过了限制。

提供一个粗略的例子(WithDegreeOrParallelism() 故意设置为 1 来说明问题):

public class Example
{
    private async Task HeavyTask(int i)
    {
        await Task.Delay(10 * 1000);
    }

    public async Task Run()
    {
        int n = 0;

        await Task.WhenAll(Enumerable.Range(0, 100)
                                     .AsParallel()
                                     .WithDegreeOfParallelism(1)
                                     .Select(async i =>
                                     {
                                         Interlocked.Increment(ref n);
                                         Console.WriteLine("[+] " + n);

                                         await HeavyTask(i);

                                         Interlocked.Decrement(ref n);
                                         Console.WriteLine("[-] " + n);
                                     }));
    }
}

class Program
{
    public static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            await new Example().Run();
        }).Wait();
    }
}

据我了解,上面的代码旨在产生如下输出:

[+] 1
[-] 0
[+] 1
[-] 0
...

而是返回:

[+] 1
[+] 2
[+] 3
[+] 4
...

建议它启动列表中的所有任务,然后等待任务返回。

是否有什么特别明显(或不明显)我做错了,这使得 WithDegreeOfParallelism() 似乎被忽略了?

【问题讨论】:

    标签: c# async-await .net-4.5


    【解决方案1】:

    更新

    抱歉,在测试您的代码后,我了解您现在看到的内容

    async i =>
    

    Async lambda 只是async void,基本上是未观察到的任务,不管Thread.CurrentThread.ManagedThreadId); 都会运行,它会清楚地告诉你它正在消耗尽可能多的线程

    还请注意,如果您的繁重任务是 IO 绑定的,那么请跳过 PLINQParallel 在 TPL 数据流 await 中使用 asyncawait ActionBlock,因为它将为您提供两全其美的效果

    例如

    public static async Task DoWorkLoads(List<Something> results)
    {
       var options = new ExecutionDataflowBlockOptions
                         {
                            MaxDegreeOfParallelism = 2
                         };
    
       var block = new ActionBlock<int>(MyMethodAsync, options);
    
       foreach (var item in list)
          block.Post(item );
    
       block.Complete();
       await block.Completion;
    
    }
    
    ...
    
    public async Task MyMethodAsync(int i)
    {       
        await Task.Delay(10 * 1000);
    }
    

    原创

    这是一个非常微妙且非常普遍的误解,但是我认为文档似乎是错误的

    设置要在查询中使用的并行度。程度 并行度是最大并发执行的任务数 将用于处理查询。

    虽然如果我们对此进行深入研究,我们会得到更好的理解,但也有关于此的 github 对话。

    ParallelOptions.MaxDegreeOfParallelism vs PLINQ’s WithDegreeOfParallelism

    PLINQ 不同。 PLINQ 中一些重要的标准查询运算符 需要处理中涉及的线程之间的通信 的查询,包括一些依赖 Barrier 来启用线程的查询 以锁步运行。 PLINQ 设计要求特定的 查询积极参与的线程数 进步。 因此,当您为 PLINQ 指定 DegreeOfParallelism 时, 您正在指定将涉及的实际线程数, 而不仅仅是一个最大值

    【讨论】:

      猜你喜欢
      • 2020-01-30
      • 2016-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-30
      • 1970-01-01
      相关资源
      最近更新 更多