【问题标题】:Task.Run Exception with non-async lambda具有非异步 lambda 的 Task.Run 异常
【发布时间】:2017-12-19 21:48:34
【问题描述】:

我正在查询数据库并将结果包装到视图模型中。测试故意错误的连接字符串,当查询在for (object result in query) 处执行时抛出异常。根据我的研究(如下),应由外部 try/catch 块处理异常,而不添加 async 作为 lambda 的关键字。但是,当我运行没有async 关键字的代码时,没有捕获到异常并且程序崩溃了。

为什么我的异常没有得到处理?

请注意,这里唯一的变化是将 async 添加到 Task.Run 的 lambda 表达式中。


According to Stephen Cleary's comment on this answer 外部try/catch 应该在没有async 的情况下捕获异常。

This echoes the first link

I have confirmed that "Break When Thrown" is disabled


更新

从评论中,我尝试了Disabling "Just my Code"

它确实允许捕获异常,但它仍然产生异常行为。

如果您在禁用“仅我的代码”的情况下运行 -async 示例,则会在返回到 catch 块之前抛出异常 15 次。


异常未被外部try/catch捕获

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace ThisQuestion
{
    class Program
    {
        static void Main(string[] args)
        {
            DoWork();

            Console.ReadLine();
        }


        private async static void DoWork()
        {
            DataContext _db = new DataContext("badconnectionstring");

            Table<Objects> objects = _db.GetTable<Objects>();

            IQueryable<object> query =
                    from o in objects
                    select o;

            try
            {
                await Task.Run
                (() =>
                {
                    foreach (object result in query) ;
                });
            }
            catch (System.Data.SqlClient.SqlException)
            {
                System.Diagnostics.Debug.WriteLine("SQLError");
            }
        }
    }

    [Table(Name="Objects")]
    class Objects
    {
        private string AColumn;
    }
}

外部try/catch捕获的异常

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Data.Linq;
using System.Data.Linq.Mapping;

namespace ThisQuestion
{
    class Program
    {
        static void Main(string[] args)
        {
            DoWork();

            Console.ReadLine();
        }


        private async static void DoWork()
        {
            DataContext _db = new DataContext("badconnectionstring");

            Table<Objects> objects = _db.GetTable<Objects>();

            IQueryable<object> query =
                    from o in objects
                    select o;

            try
            {
                await Task.Run
                (async () =>
                {
                    foreach (object result in query) ;
                });
            }
            catch (System.Data.SqlClient.SqlException)
            {
                System.Diagnostics.Debug.WriteLine("SQLError");
            }
        }
    }

    [Table(Name="Objects")]
    class Objects
    {
        private string AColumn;
    }
}

“只是我的代码”已禁用


已启用“仅我的代码”,不支持 async


启用“仅我的代码”,async

【问题讨论】:

  • 我在这里没有看到明确的问题描述,也绝对没有看到有人提出问题。如果您的意图是转到链接的帖子以查看所提出的问题,那么这是这些帖子的副本,不属于此处。
  • 没有人能告诉你为什么一个不可运行的不完整代码 sn-p 会以某种你没有描述的方式表现。如果您提供一个完整代码 sn-p,它能够实际复制所描述的行为,实际描述它的行为方式以及您期望它的行为方式,那么您可以开始接近一个可回答的问题题。可悲的是,大多数 SO 用户(至少据我所知)没有读心设备,因此不知道你想要你的代码做什么,它在做什么,或者它是什么,当你没有'没有告诉我们这些事情。
  • 我不确定我还能告诉你什么。这是一个 SQL 查询,它抛出了一个异常,我没有根据我看到的所有实施建议进行处理。
  • 运行两个 sn-ps 时,我看到两种情况下都处理了错误。他们的行为并没有不同。
  • 那么你原来的声明,即其中一个代码 sn-ps 没有处理错误是错误的,因为它只是因为你如何配置调试器而中断了调试器。如果您不希望调试器中断,您显然知道如何禁用它。

标签: c# exception asynchronous exception-handling async-await


【解决方案1】:

异常仍然没有被正确处理

我不认为这是一个正确的说法。

在两个版本的代码中,您的catch (System.Data.SqlClient.SqlException) 语句都会捕获异常。这怎么不“正确”?

您看到差异的唯一原因是 调试器 的行为方式。在第二个示例中,调试器不报告异常,因为就它而言,正在处理异常。异步 lambda 返回的 Task 对象可以观察到这一点(注意在这种情况下会调用 Task.Run(Func&lt;Task&gt;) 重载)。

在第一个代码示例中,Run() 方法正在执行直接的Action lambda。在这种情况下,发生异常的线程中没有Task对象;原来的线程中只有一个你叫Task.Run()。因此,就调试器而言,异常在发生的那个线程中未处理

因此,在第一个示例中,根据调试器的规则,异常未处理。当然,它仍然被观察到。 Task.Run()返回的Task对象封装了异常,你可以通过等待Task对象来观察它。

在第二个例子中,根据调试器的规则,异常处理。在它发生的同一线程中,由您的异步 lambda 返回的 Task 对象观察到。调试器对此很好,不会通知您。

但在这两个示例中,代码 的基本行为是相同的。您的任务抛出异常,并通过等待 Task.Run() 返回的 Task 对象被捕获在原始线程中(因为在任何一种情况下,异常都会传播到 Task 对象,只是通过不同的机制)。

注意:以上内容仅适用于您提供的两个完整代码示例。您在您的问题中有关于“15 个例外”等的额外讨论,我无法解决这些问题,因为没有 MCVE 可以重现该行为。但是假设您在两个完整的代码示例中正确地表示了基本问题,上述内容将适用于您所询问的更广泛的场景。

【讨论】:

  • 谢谢。这很有见地。 “15 Exceptions”代码只是为了替代try/catch 块。实际上,如果您使用非异步 lambda 运行版本并禁用“仅我的代码”,您会得到相同的行为......我将更新问题以反映。
  • 鉴于不同的调试器行为,调用Task.Run 的一种方法是否比另一种更“正确”(因为缺乏更好的术语)?
  • “鉴于不同的调试器行为,是调用 Task.Run 的一种方法更“正确””——恕我直言,将调试器行为与哪种类型混为一谈是错误的通话更好。忽略调试器。重要的是 lambda 是否需要使用await。如果是,那么它需要是async。如果不是,那么它不需要是async,也不应该是。如果确实需要,您可以禁用在调试器中捕获异常,但我通常更喜欢启用中断并在调试时按 F5 以从已知异常继续。
  • 再次感谢。关于“只是我的代码”行为的任何想法?
  • “对“只是我的代码”行为有什么想法吗?” -- 抱歉,我不太确定你在问什么。您可以将其用作避免调试器中断该异常的方法之一。另一种方法是右键单击“异常”窗口中的异常并选择“在用户代码中未处理时继续”(请注意,如果您在调试选项中取消选中“仅启用我的代码”,则此选项不可见)(另请注意,此选项也由 Exception Helper 中显示的复选框“Break when this exception type is user-unhandled”控制)。
猜你喜欢
  • 2018-12-09
  • 1970-01-01
  • 2013-04-20
  • 2020-11-10
  • 2022-11-30
  • 2015-09-28
  • 2016-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多