【问题标题】:Using Async/Await in Linq expression does not throw exception在 Linq 表达式中使用 Async/Await 不会引发异常
【发布时间】:2021-07-18 02:28:31
【问题描述】:

晚上,

谁能指出我为什么使用 async 和 await 运算符的 linq 语句不会将异常传播到堆栈但 foreach 循环会?必须有一些东西在引擎盖下并在某处记录这种行为,我只是没有运气找到它。 #googlefoofail #doyouevenasyncbrah

如果您运行单元测试,您将看到它返回时没有错误且有效。但是,如果您调试单元测试,它会因错误而中断,但是它将允许您继续,并且该错误永远不会传播到堆栈中。

如果您注释掉 linq 语句并取消注释 foreach 循环,则在尝试向字典添加重复键时会出现预期错误。

[TestMethod]
public void tester()
{
    List<KeyValuePair<string, string>> pairs = new List<KeyValuePair<string, string>>() 
    { 
        { new KeyValuePair<string, string>("test", "first") }, 
        { new KeyValuePair<string, string>("test", "second") } 
    };
    fails = new Dictionary<string, string>();

    pairs.ForEach(async x => await testasync(x));                //<========= does not throw error

    //foreach (KeyValuePair<string,string> pair in pairs)        //<========= throws error
    //{
    //    testasync(pair).GetAwaiter().GetResult();
    //}

    Assert.IsTrue(1 == 1);
}

private Dictionary<string, string> fails;
private async Task testasync(KeyValuePair<string, string> pair)
{
    fails.Add(pair.Key, pair.Value);
}

【问题讨论】:

标签: c# linq exception async-await


【解决方案1】:

谁能指出我为什么使用 async 和 await 运算符的 linq 语句不会将异常传播到堆栈上但 foreach 循环会传播的文档?

实际上,这不是 LINQ 语句;它是List&lt;T&gt;.ForEachList&lt;T&gt;.ForEach 不适用于 async lambda,因为它只需要 void-returning 委托类型,而不是 Task-returning 委托类型。所以async lambda 变成了async void 方法,也就是should be avoided

【讨论】:

    【解决方案2】:

    Here's some good documentation 解释您看到的行为。关键摘录:

    当异步运行的任务引发异常时,该任务会出现故障。 Task 对象保存在 Task.Exception 属性中引发的异常。错误任务在等待时会引发异常。

    由于您从未等待 Lambda 的结果,因此从未抛出存储在结果任务中的异常。您可以执行以下任何操作来解决此问题:

        // more equivalent to your `foreach` loop.
        pairs.ForEach(x => testasync(x).GetAwaiter().GetResult());
        Task.WhenAll(pairs.Select(async x => await testasync(x))).GetAwaiter().GetResult();
        // preferred
        await Task.WhenAll(pairs.Select(async x => await testasync(x)));
    

    最后一行是最好的方法,但是为了在那里使用await,你必须使调用方法async,这意味着你应该把它改成返回一个Task,任何调用它的人都应该await 返回的Task,依此类推整个调用链。这就是人们所说的“一路异步”的意思。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-07
      • 2018-09-19
      • 2013-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多