鉴于以下内容:
BlockingCollection<MyObject> collection;
public class MyObject
{
public async Task<ReturnObject> DoWork()
{
(...)
return await SomeIOWorkAsync();
}
}
同时在集合中的所有 MyObjects上异步执行所有 DoWork()任务的正确/最佳效果是什么(捕获时)返回对象),理想情况下有一个合理的线程限制(我相信Task Factory / ThreadPool在这里做了一些管理)?
答案 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 Skeet和Stephen 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 );