我正在尝试将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()似乎被忽略了?
答案 0 :(得分:1)
更新
对不起,在测试了代码之后,我了解了您现在看到的内容
async i =>
异步lambda只是async void
,无论Thread.CurrentThread.ManagedThreadId);
为何运行,基本上都是未观察到的任务,将清楚地向您显示它正在消耗自己喜欢的线程
还要注意,如果您的繁重任务是IO绑定,则跳过PLINQ
和Parallel
,在TPL数据流async
中使用await
和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);
}
原始
这是一个非常微妙和普遍的误解,但是我认为文档似乎是错误的
设置查询中要使用的并行度。程度 并行度是同时执行的最大个任务 将用于处理查询。
尽管我们对此进行了更深入的了解,但我们对此有了更好的理解。
ParallelOptions.MaxDegreeOfParallelism vs PLINQ’s WithDegreeOfParallelism
PLINQ是不同的。 PLINQ中一些重要的标准查询运算符 需要处理中涉及的线程之间的通信 的查询,包括一些依赖于屏障来启用线程的查询 锁定操作。 PLINQ设计要求特定的 主动参与查询的线程数 进展。 因此,当您为PLINQ指定一个DegreeOfParallelism时, 您指定了将涉及的实际线程数, 而不是最大。