我有一个关于异步方法的并行执行的问题。
我想限制将同时执行的异步方法的数量(以限制实际上在异步方法中发送的另一个系统的Web请求数)。
那么最好的方法是什么? 我通过使用Parallel找到了解决方案,并设置了DegreeOfParallelism,但我不太喜欢这个解决方案,因为它会阻止线程数等于DOP(并行度)。
所以,这是使用Parallel的代码:
var results = dataProvider.GetResults(someIdParam);
var average = results.AsParallel().WithDegreeOfParallelism(5).Average(x =>
{
var t = GetSSIM(x);
t.Wait();
return t.Result;
});
因此,这将起作用并限制同时发出的请求数,但会阻塞5个线程。
我最终编写了自己的方法:
public static async Task<IEnumerable<T2>> ProcessEachAsync<T1, T2>(IEnumerable<T1> src, Func<T1, Task<T2>> func, int dop)
{
var workers = new Task<T2>[Math.Min(dop, src.Count())]; //to make sure that we will not have nulls in workers collection
var result = new List<T2>();
int counter = 0;
int index = 0;
foreach(var element in src)
{
if (counter < dop)
index = counter++;
else
{
var ended = await Task.WhenAny(workers);
index = Array.FindIndex(workers, x => x == ended);
result.Add(ended.Result);
}
var t = func(element);
t.Start();
workers[index] = t;
}
Task.WaitAll(workers);
result.AddRange(workers.Select(x => x.Result));
return result;
}
小心!!!!!此代码尚未经过测试且存在错误! 但它解释了主要想法
因此,此解决方案将仅阻止1个线程。也许有更简单的方法来达到我想要的目的?
答案 0 :(得分:0)
感谢@evk和我的同事帮我解决了这个问题。 所以我用SemaphoreSlim实现了一个解决方案。它的缺点是它将所有数据转换为任务,但代码非常漂亮,我将留下它:)
public static async Task<IEnumerable<T2>> ProcessEachAsync<T1, T2>(IEnumerable<T1> src, Func<T1, Task<T2>> func, int dop)
{
using (var semSlim = new SemaphoreSlim(dop))
{
var result = new ConcurrentBag<T2>();
Func<T1, Task> getTask = async (x) =>
{
try
{
await semSlim.WaitAsync();
var res = await func(x);
result.Add(res);
}
finally
{
semSlim.Release();
}
};
await Task.WhenAll(src.Select(x => getTask(x)));
return result;
}
}
<强>小心!没有测试!!!!
谢谢大家!