4

鉴于以下情况:

BlockingCollection<MyObject> collection;

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

什么是同时在集合中的所有MyObjects上异步执行所有DoWork() 任务的正确/最高效的方法(同时捕获返回对象),但理想情况下具有合理的线程限制(我相信任务工厂/线程池在这里做了一些管理)?

4

4 回答 4

6

您可以使用WhenAll 扩展方法。

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

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

于 2012-12-17T07:57:18.407 回答
3

ThreadPool管理正在运行的线程Task数,但这对异步s没有多大帮助。

正因为如此,你需要别的东西。一种方法是利用ActionBlockTPL 数据流:

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;
于 2012-12-17T12:03:36.073 回答
1

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

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

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

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

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

是的,您可以使用Jon SkeetStephen Toub描述的方法。我的 AsyncEx 库(可通过 NuGet 获得)中有一个类似的解决方案,您可以像这样使用它:

// "tasks" is of type "Task<ReturnObject>[]"
var tasks = collection.Select(x => x.DoWork()).OrderByCompletion();
foreach (var task in tasks)
{
  var result = await task;
  ...
}
于 2012-12-17T14:56:22.907 回答
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 );
于 2012-12-17T09:18:30.537 回答