[英]Threading foreach that abides by async/await methods inside said foreach
[英]What is the proper way to await for async inside of a ForEach?
.NET 核心 2.2,VS 2019
问题是, UpdateDatabaseWithFile
永远不会被执行? 我在这里做错了什么? 我试图用等待包装Directory
,但我不能,因为它返回 void。 这样做的正确方法是什么?
这是代码:
Directory
.GetFiles(@"C:\Temp")
.ToList()
.ForEach(async file =>
{
await this._dataService.UpdateDatabaseWithFile();
});
这是因为ForEach<T>
只是执行该async
函数,但没有等待它们完成。
您可以使用Select
来投影必须执行的Task
,然后使用Task.WhenAll()
执行该功能并等待它们完成:
var tasks = Directory
.GetFiles(@"C:\Temp")
.Select(async file =>
{
await this._dataService.UpdateDatabaseWithFile();
});
await Task.WhenAll(tasks);
或者,如果您想按顺序执行函数,则可以使用简单的foreach
代替Task.WhenAll()
:
foreach (var file in Directory.GetFiles(@"C:\Temp"))
{
await this._dataService.UpdateDatabaseWithFile();
}
补充说明:
这是ForEach<T>
的实现:
for(int i = 0 ; i < array.Length; i++) {
action(array[i]);
}
如您所见,它只是执行该操作,但没有等待它们完成。 实际上,它不能。 Action
什么也不返回。 但是等待Task
必须返回。 因此,为了使ForEach
函数在这种情况下有用,它必须采用返回Task
的Func
然后在内部for
迭代器。
List.ForEach
方法不理解异步委托,换句话说,它没有接受Func<Task<T>>
参数的重载。 在这些情况下,所提供的 async lambda 被视为async void
。 异步无效是邪恶的,因为它们不能被等待,所以它们的执行不能与程序的其他部分协调。 它们类似于即发即弃的任务,但有一个额外的缺点是在发生异常时终止进程。
void-returning async 方法有一个特定的目的:使异步事件处理程序成为可能。
您应该注意您尝试使用异步委托作为参数调用的方法的签名。 像Task.Run
这样的一些方法对异步委托有特定的重载,并且可以很好地处理它们。 像 LINQ Select
这样的其他方法在设计时并未考虑到异步等待,但至少它们接受Func
类型的参数而不是Action
,因此生成的任务被返回并且可以被正确地收集和等待。 像List.ForEach
带有Action
参数的方法不应该与异步委托一起使用。
这里最可能的问题是.ForEach()
无法让您实际等待操作完成。 它只会执行它们并继续前进。
Eric Lippert 有一篇很好的博客文章说明了为什么.ForEach()
方法不是那么好,这是一个完美的例子,说明了foreach
循环如何优于.ForEach
- foreach
将允许您实际等待操作。
只需使用循环,您的问题就会消失:
foreach (var file = Directory.GetFiles(@"C:\Temp"))
{
await this._dataService.UpdateDatabaseWithFile();
}
应该注意的是,您似乎实际上并没有在任何地方使用file
变量,这似乎是一个问题。 希望只是一个错字?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.