你如何为异步/等待做出一个沉默的例外?

时间:2016-03-13 10:24:54

标签: c# exception-handling async-await

我听说过火灾中的例外情况。忘记了异步调用被吞下。但是,我不会遇到以下示例的情况:

class Program
{
    static void Main(string[] args)
    {
        foo();
        Thread.Sleep(3000);
    }

    static async void foo()
    {
        Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                throw new Exception();
            });
    }
}

抛出异常,导致失败快速。我不知道GC是否会在收集对象时引发异常。

在什么样的代码示例中,开发人员在异步/等待代码中遇到未观察到的,无声的异常?

2 个答案:

答案 0 :(得分:0)

是否吞下任务异常取决于.NET版本和设置。从4.5开始,它们默认被忽略。请勿更改该默认值。不要更改流程范围的设置。相反,与时俱进。

GC会触发快速失败行为。不要依赖它。

在您的示例中,您使用了异步void,它再次改变了行为。它会向当前同步上下文发布异常。在这里,没有。不要使用async void。

在异步方法中嵌套Task.Factory.StartNew会创建一个完全独立的任务。您的异步方法没有意义。

答案 1 :(得分:0)

当正在运行的任务内部发生未处理的异常时,它会被存储直到被观察到。有几种方法可以观察未处理的任务异常。调用irb(main):014:0> @shipment.save (0.3ms) BEGIN SQL (0.4ms) INSERT INTO `shipments` (`name`, `from_country`, `from_city`, `from_state`, `from_postal_code`, `from_street_address`, `to_country`, `to_city`, `to_state`, `to_postal_code`, `to_street_address`, `service_code`, `created_at`, `updated_at`) VALUES ('121', 'US', 'ds Ferry', 'NY', '33-1716', 'd, 329', 'US', 'ss', 'ss', '1234-1228', 'r Avenue, 343', 'GROUND', '2016-03-14 15:04:15', '2016-03-14 15:04:15') SQL (5.0ms) INSERT INTO `orders` (`created_at`, `updated_at`) VALUES ('2016-03-14 15:04:15', '2016-03-14 15:04:15') SQL (0.3ms) INSERT INTO `shippings` (`shipment_id`, `order_id`, `created_at`, `updated_at`) VALUES (3, 6, '2016-03-14 15:04:15', '2016-03-14 15:04:15') SQL (0.3ms) INSERT INTO `shippings` (`shipment_id`, `order_id`, `created_at`, `updated_at`) VALUES (3, 6, '2016-03-14 15:04:15', '2016-03-14 15:04:15') (4.1ms) COMMIT => true ,获取Wait() Exception个对象或获取Task Result个对象。

  

如果您没有给出有故障的任务提供传播其例外的机会>运行时会   根据当前情况升级任务的未观察到的异常   任务被垃圾收集时的.NET异常策略。未观测到   最终会在终结器中观察到任务异常   线程上下文。如果在执行Finalize期间抛出未处理的异常   方法,默​​认情况下,运行时将终止当前进程,   并且没有活动的try / finally块或其他终结器   已执行,包括释放非托管资源句柄的终结器。

来自Task<T>本书。

但是,从.NET 4.5开始,默认行为是未被观察到的异常被吞没&#34;:不会在终结上下文中抛出,并且不会终止进程。它可以通过以下配置标志进行更改:

Parallel Programming with Microsoft .NET

Async / await方法由编译器转换为使用<configuration> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration> 个对象的代码,特别是Task<T>将被翻译成类似的东西:

await

在内部将调用上述之一:(.. everything before await...).ContinueWith(...the rest of the method...) Wait()并观察异常。但是,当你跑步&#34;火&amp;忘记&#34;使用Resultawait的任务(使用Task.Run时不是这种情况)并且从不等待或等待它,任何未处理的异常将被忽略。因此,一般来说,这被认为是一种不好的做法。