我的异步任务库应该安静地吞下异常吗?


10

我刚刚了解到.NET 4.5引入了对a内部异常Task处理方式的更改。即,它们被安静地压制。

这样做的官方理由似乎是“我们希望对没有经验的开发人员更加友好”:

在.NET 4.5中,任务比在.NET 4中具有更大的重要性,因为它们被支持在C#和Visual Basic语言中,成为语言支持的新异步功能的一部分。这实际上使Tasks脱离了经验丰富的开发人员的领域,进入了每个人的领域。结果,它也导致了关于异常处理的严格程度的一系列新折衷。

来源

我已经学会相信,.NET中的许多决定都是由真正知道自己在做什么的人做出的,并且通常在他们做出决定的背后有很好的理由。但是这个逃脱了我。

如果我正在设计自己的异步任务库,那么吞没框架开发人员看到的异常的吞咽异常有什么好处?


4
+1。鉴于没有传播异常,您无法处理的事实是一个好问题,即使撇开异步上下文也是如此。另外,恕我直言,关于没有经验的开发人员的争论非常la脚。对于初学者来说,除了一段既行不通,又不抛出异常的代码之外,还有什么比尴尬的了?
Arseni Mourzenko 2013年

@MainMa确实是我的想法,但是我以前是错的(事实证明它们确实有很好的但不明显的原因),所以我想问一下。
Roman Starkov

2
哇,很高兴您发布此消息,只是因为这是我第一次听说。这绝对违反POLA。您是对的,这些人确实知道他们在做什么,所以我很想知道背后的原因可能是什么...不知道它是否基于Async的实现以及异常将如何传播而产生了更多的技术原因。作为底部被传递到协程..
吉米·霍法

Answers:


2

值得一提的是,链接到的文档提供了一个示例案例作为理由:

Task op1 = FooAsync(); 
Task op2 = BarAsync(); 
await op1; 
await op2;

在此代码中,开发人员将启动两个异步操作以并行运行,然后使用新的等待语言功能异步等待每个操作……考虑如果op1和op2都出错,将会发生什么。等待op1将传播op1的异常,因此将永远不会等待op2。结果,将不会观察到op2的异常,并且该过程最终将崩溃。

为了使开发人员更容易编写基于任务的异步代码,.NET 4.5更改了未观察到的异常的默认异常行为。尽管未观察到的异常仍将引发UnobservedTaskException事件(不这样做将是一个重大更改),但默认情况下该过程不会崩溃。相反,无论事件处理程序是否观察到异常,异常都会在引发事件后最终被吞噬。

我对此不服气。它消除了明确但难以跟踪的错误(可能在实际错误发生很长时间之后发生的神秘程序崩溃)的可能性,但用完全无提示的错误替代了它-以后可能同样难以跟踪问题在您的程序中。对我来说,这似乎是一个可疑的选择。

该行为是可配置的-但是,当然,有99%的开发人员将只使用默认行为,而不用考虑此问题。因此,他们选择的默认值很大。


但是您确实有的正常例外op1,对吗?如果一个遗体未处理的,它带来下跌过程中,对不对?
Timwi

1
据我了解,@ Timwi op1的异常和op2异常都不会降低程序的质量。好处是您有机会观察到两者。但是,如果您不这样做,它们都会被吞下。我可能是错的。

让他们“观察”到两者如此重要让我感到非常惊讶。如果您想“观察”它们,则可以捕获它们并通过任务结果报告它们的发生!...同意您的推理,+ 1
Roman Starkov

2

TL; DR- ,您不应该只是悄悄地忽略异常。


让我们看看这些假设。

  • 你的盲目信仰

我已经学会相信,.NET中的许多决定都是由真正知道自己在做什么的人做出的,并且通常在他们做出决定的背后有很好的理由。但是这个逃脱了我。

毫无疑问,MS的工作人员确实非常聪明。他们还拥有许多……无能为力并且一贯做出错误决定的经理和高管。尽管我们想相信技术精明的科学家推动产品开发的童话,但现实却更加严峻。

我的意思是,如果您的直觉告诉您某些问题可能是错误的,那么很有可能(以及过去的先例)这是错误的。

  • 这是一个技术问题

在这种情况下,如何处理例外情况是人为因素决定的,而不是技术决定。松散地,异常是一个错误。即使格式不正确,错误的表示也可以为最终用户提供重要信息。也就是说,出了点问题。

考虑一下最终用户与开发人员之间的这种假设对话。

用户:该应用程序已损坏。
开发人员:怎么了?
用户:我不知道,我单击按钮没有任何反应。
开发人员:您什么意思什么都没发生?
用户:看,我单击,我等待,然后我等待,然后我等待,什么都没有...显然已经坏了。

我们可怜的,幸运的是,假设的开发人员现在必须找出事件链中出了什么问题。在哪
事件处理程序通知->处理事件的例程->处理程序触发的方法->异步调用的开始-> OSI网络的7层->物理传输->备份OSI网络的7层->接收服务->服务调用的方法-> ...->返回服务发送的回复-> ....->收到收据->处理收到的回复-> ...

并且请注意,我已经掩盖了几个潜在的错误路径。


在解决了一些基本假设之后,我认为现在变得更加清楚为什么安静地抑制异常是一个坏主意。异常和相关的错误消息是向用户指示发生问题的关键指示器。即使该消息对最终用户而言毫无意义,但嵌入的信息对于开发人员了解问题所在也很有用。如果他们很幸运,该消息甚至可以引导您找到解决方案。

我认为问题的一部分在于,错误处理的异常将使您的应用程序崩溃。通过抑制异常,应用程序将不会崩溃。这样做有一定的道理,因为异步的目的是允许应用程序在检索数据时保持工作状态。逻辑上的扩展并不是说,如果您可以在等待结果的同时继续操作,那么检索失败也不会使应用程序崩溃-异步调用被隐式声明为不足以终止应用程序。

因此,这是一个解决方案,但它正在解决错误的问题。为异常处理提供包装器并不需要花费太多精力,从而使应用程序可以保持运行。捕获异常,将错误消息抛出到屏幕或记录下来,并允许应用程序继续运行。抑制异常意味着忽略错误将是可以的,并且您的测试将确保异常永远不会逃逸到现场。

因此,我的最终评估是,这是一个半生半熟的尝试,目的是解决在人们还没有准备好将思维转向该方法时将他们推入异步模型所产生的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.