如何在循环中调用异步方法而不等待?

时间:2014-07-29 06:23:41

标签: c# .net asynchronous recursion async-await

考虑这段代码,其中有一些工作在for循环中完成,然后是一个递归调用来处理子项。我想将DoSomething(item)和GetItems(id)转换为异步方法,但如果我在这里等待它们,for循环将等待每次迭代完成后再继续,基本上失去了并行处理的好处。我怎样才能提高这种方法的性能?是否可以使用async / await做到这一点?

public void DoWork(string id)
        {            
                var items = GetItems(id);  //takes time

                if (items == null)
                    return;

                Parallel.ForEach(items, item =>
                {
                    DoSomething(item); //takes time
                    DoWork(item.subItemId);                    
                });           
        }

1 个答案:

答案 0 :(得分:3)

您可以创建一系列任务,然后使用Task.WhenAll等待所有任务完成,而不是使用Parallel.ForEach遍历项目。由于您的代码也涉及递归,因此稍微复杂一点,您需要将DoSomethingDoWork合并到一个我恰当地命名为DoIt的方法中:

async Task DoWork(String id) {
  var items = GetItems(id);
  if (items == null)
    return;
  var tasks = items.Select(DoIt);
  await Task.WhenAll(tasks);
}

async Task DoIt(Item item) {
  await DoSomething(item);
  await DoWork(item.subItemId);
}

混合Parallel.ForEach和async / await是一个坏主意。 Parallel.ForEach将允许您的代码并行执行,对于计算密集但可并行化的算法,您将获得最佳性能。但是,async / await允许您的代码并发执行,例如,在IO操作中阻止的重用线程。

简化Parallel.ForEach将设置与计算机上具有CPU内核一样多的线程,然后对要迭代的项进行分区,以便跨这些线程执行。所以Parallel.ForEach应该在你的调用堆栈的底部使用一次,然后它将把工作分散到多个线程并等待它们完成。在每个线程内以递归方式调用Parallel.ForEach只是疯狂,根本无法提高性能。