使用异步的 List<T>.ForEach() 有何意义?

5
我遇到了下面这段代码:
items.ForEach(async item =>
{
     doSomeStuff();   
     await mongoItems.FindOneAndUpdateAsync(mongoMumboJumbo);
     await AddBlah(SqlMumboJumbo);
});

将这个循环变成 .forEach 委托有意义吗?还是可以只用普通的 foreach 循环?只要包含循环的函数是异步的,那么默认情况下就是异步的。


它比普通的foreach循环更简洁,更不啰嗦。 - krillgar
2个回答

7
< p > ForEach 接收的委托是一个 Action<T>

public void ForEach(Action<T> action)

这意味着内部使用的任何异步委托都会有效地转换为async void方法。这些是"fire and forget"方式的执行。这意味着您的foreach在继续调用列表中的下一个项目上的委托之前不会以异步等待的方式完成,这可能是不期望的行为。
请改用常规的foreach
副注 - Eric Lippert的foreach VS ForEach,是一篇很棒的博客文章。

这正是我正在寻找的。我现在使用控制器方法的原因是由于多线程/线程尝试使用相同的db上下文错误。我并不完全理解 - 但是通过委托,它尝试异步地遍历每个项目,而不是按顺序进行?编辑:哈哈,这不是我第一次在涉及C#时来到Eric的博客。一旦我得到答案,我会回来接受答案,谢谢。 - VSO
1
好的回答!这里是另一个对OP有用的链接:传递异步lambda时要避免的潜在陷阱 - sstan
只是将代码从items.ForEach切换到foreach循环,似乎修复了所有的异步错误。如果后续测试失败,我会进行更新,但现在看来已经解决了问题。现在只需完全理解它.. - VSO

2

您不知道函数何时完成,也不知道函数的结果。如果您在单独的任务中启动每个计算,则可以等待Task.WhenAll并解释结果,甚至捕获异常:

private async Task ActionAsync(T item)
{
    doSomeStuff();   
    await mongoItems.FindOneAndUpdateAsync(mongoMumboJumbo);
    await AddBlah(SqlMumboJumbo);
}

private async Task MyFunction(IEnumerable<T> items)
{
    try
    {
        foreach (var item in items)
        {
            tasks.Add( ActionAsync(item) )
        }
        // while all actions are running do something useful
        // when needed await for all tasks to finish:
        await Task.WhenAll(tasks);
        // interpret the result of each action using property Task.Result
    }
    catch (AggregateException exc)
    {
        ProcessAggregateException(exc);
    }
}

当您的任务抛出异常时,将抛出AggregateException。它包含所有任务抛出的异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接