我使用的API客户端是完全异步的,也就是说,每个操作要么返回任务或任务<T>,例如:
static async Task DoSomething(int siteId, int postId, IBlogClient client)
{
await client.DeletePost(siteId, postId); // call API client
Console.WriteLine("Deleted post {0}.", siteId);
}
使用c# 5 async/await操作符,启动多个任务并等待它们全部完成的正确/最有效的方法是什么:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());
or:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());
由于API客户端在内部使用HttpClient,我希望它立即发出5个HTTP请求,每一个请求完成时都写入控制台。
int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());
尽管您将这些操作与上面的代码并行运行,但此代码将阻塞每个操作所运行的每个线程。例如,如果网络调用需要2秒,每个线程挂起2秒,不做任何事情,只是等待。
int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());
另一方面,上面带有WaitAll的代码也会阻塞线程,在操作结束之前,线程将无法自由处理任何其他工作。
推荐的方法
我更喜欢WhenAll,这将在并行异步执行您的操作。
public async Task DoWork() {
int[] ids = new[] { 1, 2, 3, 4, 5 };
await Task.WhenAll(ids.Select(i => DoSomething(1, i, blogClient)));
}
事实上,在上面的例子中,你甚至不需要等待,你可以直接从方法中返回,因为你没有任何延续:
DoWork()
{
Int [] id = new[] {1,2,3,4,5};
返回Task.WhenAll (id。Select(i => DoSomething(1, i, blogClient));
}
为了支持这一点,这里有一篇详细的博客文章
使用ASP的并发异步I/O的方式和位置。NET Web API
这个问题已经10年了,OP问的是c# 5。
到今天为止,还有一个选择:并行。在。net 6中引入的ForEachAsync方法。
下面是一个基于OP代码的例子:
int[] ids = new[] { 1, 2, 3, 4, 5 };
await Parallel.ForEachAsync(ids, async (i,token) => await DoSomething(1, i, blogClient));
这是完全异步的,不会阻塞任何线程。
此外,它可能比Task更好。WaitAll和Task。WhenAll接近,因为它们不限制并行运行的线程数量。如果你有一个巨大的数组,它会消耗掉你所有的RAM。平行的。ForEachAsync允许你像这样指定并行度:
var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
await Parallel.ForEachAsync(ids, options, async (i,token) => await DoSomething(1, i, blogClient));
通过这种方式,只有4个线程并行运行。