使用非异步lambda的Task.Run异常

时间:2017-07-14 17:49:08

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

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

为什么我的例外没有被处理?

请注意,此处唯一的更改是在async的lambda表达式中添加Task.Run

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"

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

如果您运行 - 同步示例,而#34; Just My Code"如果禁用,则在返回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;
    }
}

&#34; Just My Code&#34;停用 enter image description here

&#34; Just My Code&#34;已启用,无async enter image description here

&#34; Just My Code&#34;启用,w / async enter image description here

1 个答案:

答案 0 :(得分:2)

  

异常仍未正确处理

我认为这不是正确的陈述。

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

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

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

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

在第二个示例中,通过调试器的规则,处理异常。通过异步lambda返回的Task对象在它发生的同一个线程中观察到它。调试器没问题,也没有通知你。

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

注意:以上内容仅适用于您提供的两个完整代码示例。您在关于&#34; 15例外&#34;等的问题中还有其他讨论,我无法解决,因为没有MCVE可以再现该行为。但假设您在两个完整的代码示例中正确表示了基本问题,则上述内容将适用于您所询问的更广泛的场景。