我正在玩.Net中并行执行任务。我已经实现了下面的函数,它使用Task.WhenAll并行执行任务列表。我还发现我可以使用两个选项在列表中添加任务。选项1是使用Task.Run并传递Func委托。选项2是添加调用的Func委托的结果。
所以我的问题是:
您认为处理异步并行任务的最佳方法是什么?
public static async Task<IEnumerable<TResult>> ExecTasksInParallelAsync<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, Task<TResult>> task, int minDegreeOfParallelism = 1, int maxDegreeOfParallelism = 1)
{
var allTasks = new List<Task<TResult>>();
using (var throttler = new SemaphoreSlim(minDegreeOfParallelism, maxDegreeOfParallelism))
{
foreach (var element in source)
{
// do an async wait until we can schedule again
await throttler.WaitAsync();
Func<Task<TResult>> func = async () =>
{
try
{
return await task(element);
}
finally
{
throttler.Release();
}
};
//Option 1
allTasks.Add(Task.Run(func));
//Option 2
allTasks.Add(func.Invoke());
}
return await Task.WhenAll(allTasks);
}
}
上述功能以
执行 [HttpGet()]
public async Task<IEnumerable<string>> Get()
{using (var client = new HttpClient())
{
var source = Enumerable.Range(1, 1000).Select(x => "https://dog.ceo/api/breeds/list/all");
var result = await Class1.ExecTasksInParallelAsync(
source, async (x) =>
{
var responseMessage = await client.GetAsync(x);
return await responseMessage.Content.ReadAsStringAsync();
}, 100, 200);
return result;
}
}
答案 0 :(得分:0)
我使用您的代码运行了一些测试,并确定选项2比选项1大约快50倍,至少在我的机器上。但是,使用PLINQ甚至比选项2快10倍。
你可以用一行PLINQ代替整个混乱:
return source.AsParallel().WithDegreeOfParallelism(maxDegreeOfParallelism)
.Select( s => task(s).GetAwaiter().GetResult() );
糟糕...选项4
如果task
实际上是异步的(我用虚拟同步函数进行测试),那么我的先前解决方案会降低并行性。此解决方案解决了问题:
var tasks = source.AsParallel()
.WithDegreeOfParallelism(maxDegreeOfParallelism)
.Select( s => task(s) );
await Task.WhenAll(tasks);
return tasks.Select( t => t.Result );
我在笔记本电脑上运行了10,000次迭代。我做了三次运行以确保没有启动效果。结果:
Run 1
Option 1: Duration: 13727ms
Option 2: Duration: 303ms
Option 3 :Duration: 39ms
Run 2
Option 1: Duration: 13586ms
Option 2: Duration: 287ms
Option 3 :Duration: 28ms
Run 3
Option 1: Duration: 13580ms
Option 2: Duration: 316ms
Option 3 :Duration: 32ms
你可以try it on DotNetFiddle但是你必须使用更短的时间才能保持在配额范围内。
除了允许非常简短且功能强大的代码之外,PLINQ完全杀死它以进行并行处理,如LINQ uses a functional programming approach和功能方法is way better for parallel tasks。