具有异步任务方法的对象列表,同时执行所有操作

时间:2012-12-17 07:53:53

标签: c# concurrency task-parallel-library .net-4.5 async-await

鉴于以下内容:

BlockingCollection<MyObject> collection;

public class MyObject
{
   public async Task<ReturnObject> DoWork() 
   {       
      (...) 
      return await SomeIOWorkAsync();
   }
}

同时在集合中的所有 MyObjects上异步执行所有 DoWork()任务的正确/最佳效果是什么(捕获时)返回对象),理想情况下有一个合理的线程限制(我相信Task Factory / ThreadPool在这里做了一些管理)?

4 个答案:

答案 0 :(得分:6)

您可以使用WhenAll扩展方法。

var combinedTask = await Task.WhenAll(collection.Select(x => x.DoWork());

它将同时启动所有任务并等待所有任务完成。

答案 1 :(得分:3)

ThreadPool管理正在运行的线程的数量,但这对异步Task没什么帮助。

因此,你还需要别的东西。一种方法是使用TPL Dataflow中的ActionBlock

int limit = …;
IEnumerable<MyObject> collection = …;

var block = new ActionBlock<MyObject>(
    o => o.DoWork(),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = limit });

foreach (var obj in collection)
    block.Post(o);

block.Complete();
await block.Completion;

答案 2 :(得分:1)

  

同时在集合中的所有MyObjects上异步执行所有DoWork()任务的正确/最佳性能是什么(在捕获返回对象时),理想情况下具有合理的线程限制

最简单的方法是使用Task.WhenAll

ReturnObject[] results = await Task.WhenAll(collection.Select(x => x.DoWork()));

这将在集合中的所有DoWork上调用MyObjects,然后等待它们全部完成。线程池明智地处理所有限制。

  

如果我想立即捕获每个单独的DoWork()而不是等待所有项目完成,是否有不同的方式?

是的,您可以使用Jon SkeetStephen Toub描述的方法。我的AsyncEx库中有similar solution(可通过NuGet获得),您可以这样使用:

// "tasks" is of type "Task<ReturnObject>[]"
var tasks = collection.Select(x => x.DoWork()).OrderByCompletion();
foreach (var task in tasks)
{
  var result = await task;
  ...
}

答案 3 :(得分:0)

我的评论有点神秘,所以我想添加这个答案:

List<Task<ReturnObject>> workTasks =
  collection.Select( o => o.DoWork() ).ToList();

List<Task> resultTasks =
  workTasks.Select( o => o.ContinueWith( t =>
      {
        ReturnObject r = t.Result;
        // do something with the result
      },
      // if you want to run this on the UI thread
      TaskScheduler.FromCurrentSynchronizationContext()
    )
  )
  .ToList();

await Task.WhenAll( resultTasks );