ton*_*314 0 c# multithreading async-await parallel.foreach
我有一个长期运行的工作,我需要为集合中的每个项目运行一次。
我想同时做这些工作,虽然整个程序可以等待所有工作完成。(稍后我可能会更改它,但就目前而言,我让问题保持简单)
根据已经提供的一些帮助,我得到了以下模式:
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
}
private async void DoStuffWithItems(ICollection<MyItem> items)
{
var tasks = items.Select (i => DoStuffWithItem(i));
await Task.WhenAll(tasks);
}
private Task DoStuffWithItem(MyItem item)
{
//LongRunningTask
return Task.Run(async () =>
{
var returnObject = await LongRunningAsyncMethod(item);
});
}
Run Code Online (Sandbox Code Playgroud)
如果我理解正确的话,这仍然一次完成每项任务 - 毫无意义。
有人建议我将 Parallel.ForEach 与 async await 结合使用 - Parallel-ForEach 模式很简单:
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
Parallel.ForEach(items, (item) =>
{
var returnObject = LongRunningMethod(item);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我理解正确,我将为每个项目启动一个单独的线程来做它的事情,但是当它这样做时,主线程将等待
但是,如果我希望在 DoWork 等待 Parallel.ForEach 完成时继续执行程序的其余部分,该怎么办?
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
DoINeedThisMethod(items);
}
private async void DoINeedThisMethod(ICollection<MyItem> items)
{
await Task.Run(() => {
DoStuffWithItems(items);
});
}
private async void DoStuffWithItems(ICollection<MyItem> items)
{
var newobjs = new List<MyItem>();
Parallel.ForEach(items, (item) =>
{
var bc = LongRunningAsyncMethod(item);
});
}
Run Code Online (Sandbox Code Playgroud)
感觉不太对劲——我在那里有一个异步空洞,这不是一个好习惯。但是我在这里是正确的吗?如果我理解正确的话,在 DoINeedThisMethod 中,我正在启动一个新线程(通过 Task)来执行 Parallel.ForEach 调用,但是在那里使用“wait”意味着主线程现在将继续,而 Parallel.ForEach完成。
实际上,你的第一个假设
如果我理解正确的话,这仍然一次完成每项任务
很容易证明是错误的:
private static async Task Main(string[] args)
{
async Task DoStuffWithItem(int i)
{
// long running task
Console.WriteLine($"processing item {i} started");
await Task.Delay(500);
Console.WriteLine($"processing item {i} finished");
}
var items = new List<int> { 1, 2, 3 };
var tasks = items.Select(i => DoStuffWithItem(i)).ToList();
await Task.WhenAll(tasks);
Console.WriteLine("\nFinished");
}
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
processing item 1 started
processing item 2 started
processing item 3 started
processing item 2 finished
processing item 1 finished
processing item 3 finished
Finished
Run Code Online (Sandbox Code Playgroud)
所以任务是并发执行的......
注意:正如下面的评论中所解释的,“ToList()”会导致立即执行 Linq 查询(立即启动任务)。如果没有 'ToList()',任务只会在执行 'await Task.WhenAll(tasks)' 时启动