我听说过火灾中的例外情况。忘记了异步调用被吞下。但是,我不会遇到以下示例的情况:
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是否会在收集对象时引发异常。
在什么样的代码示例中,开发人员在异步/等待代码中遇到未观察到的,无声的异常?
答案 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;使用Result
或await
的任务(使用Task.Run
时不是这种情况)并且从不等待或等待它,任何未处理的异常将被忽略。因此,一般来说,这被认为是一种不好的做法。