c#异步lambda环境中的错误处理策略
本文关键字:错误 处理 策略 异步 lambda 环境 | 更新日期: 2023-09-27 18:08:18
我曾经编写过大量的c#客户端代码,这些代码会在引擎室抛出异常(比如网络故障),并期望上面的人来处理它。最终会产生一个应用范围内的try/catch来处理剩余的问题("foo栏中出现意外异常,点击查看详细信息,联系技术支持...."),再加上一个应用范围内的未处理异常处理程序,以防万一。
现在我正在用Silverlight写一个"厚"客户端,我不知道该怎么做了。我的大量机舱代码作为网络事件驱动的回调运行,然后调用原始调用者提供给我的lambda;但它们早已不复存在。我不能对它们抛出异常,因为没有堆栈可以冒泡。我不能强迫他们放松。它们仍然处于挂起状态(不管这对特定的异步调用意味着什么)。
我看到AsyncEventCompletedArgs与它的RaiseExceptionIfNeeded技巧。使用它似乎将我的低级管道暴露给高级代码(想想MVVM, MVC,…)。我想帮助上层代码在默认情况下"做正确的事情",就像他们在过去的同步/异常驱动时代所做的那样。但这无济于事。它们从AsyncArgs实例中读取结果,抛出并且回调终止,使它们在逻辑上仍然悬空
无论如何-希望这是有意义的。有没有人有想要分享的设计或经验?什么是任何的SL MVVM, MVC框架做?
注意-我不是在谈论c#5中那些花哨的Async新东西(那会有帮助吗?)
(可能会被关闭,因为不够具体,太主观,等等,叹息)
您可以让调用者为成功完成提供一个lambda,也可以为在代码中捕获异常的情况提供一个异常。
响应扩展允许在订阅可观察对象时这样做。
类似以下语句的内容:
asyncCall.Subscribe(result => DoSomething(result), ex => Oops(ex));
如果您的需求包括重要的并行杰出异步操作,那么就不要再阅读这个答案了。但是,如果操作是顺序调用的(一个操作直到另一个操作完成才调用),那么您可能会发现这里提到的东西有点用处。
在Silverlight中处理异步代码是我非常感兴趣的主题。我已经写了几篇关于我称之为AsynchOperationService
的博客,它使代码能够使用简单的同步顺序样式编写,并包括对错误处理的良好支持。
从这个列表中挑出来。
Rx可能是一个选项,它看起来确实很强大。尽管它很复杂,但它最初的设计目的是处理传入事件流。我不相信(尽管我承认我的研究仍然很肤浅)它很适合"开始某事,稍后回应某事完成"的操作。
我个人喜欢简单和直接的东西,但很明显,我需要完全改变我的编码方式,以适当地包括Rx的功能,我相当确定最终的代码不会那么容易阅读。
而AsyncOperationService
具有非常轻的足迹,一旦我创建了一些有用的单行函数,生成的代码序列非常容易阅读。
那么有什么问题呢?好吧,有一个,它要么是你需要的,要么是与你需要的完全相反的。该方法的工作原理是假设一系列异步操作需要按顺序完成。如果您的要求是重要的并行操作,那么在其当前形式AsyncOperationService
不适合您(尽管这可能会改变)。
技巧是让Exception返回到UI线程。你仍然想要呕吐,直到有人像以前一样处理它,但是你必须在线程的顶层捕获它,并将它传递回主线程,然后像往常一样处理它。有几种方法可以做到这一点,在Silverlight中最简单的是使用BackgroundWorker,但最原始的方法是为所有返回值或错误的异步方法提供一个回调。像这样:
DoAsync(
() => BackgroundThreadWork(), // executed on background thread
result => { // executed on UI thread
if(result.Error != null)
throw result.Error;
HandleResultOnUIThread(result.Value);
});
下面是一个非常简单的异步动作队列的例子,可以很容易地转换成Silverlight友好的版本:
MetaSharp中的Simple ActionQueue
我正在使用队列,因为它允许我保证在后台线程上完成的工作顺序。当你有一堆异步操作时,很容易进入竞争条件,因此,队列是一个很好的结构,可以保证它们的执行顺序,即使它们实际上都是异步的。
我使用以下结构的代码来异步处理异常问题:
void ClientAsyncOperationCompleted(object sender, AsyncOperationEventArgs e)
{
if (e.Error == null) {
//Normal execution path
}
else {
//you can put your structured exception handling code here around e.Error
if(e.Error is ConcreteException) {
//concrete exception handling
}
else {
//general exception handling
}
}
}
如果方法是异步执行的,没有办法得到同步异常抛出,除非我们包装异步方法并使用一些线程同步结构(如ManualResetEvent
)同步其执行。所以如果我们接受异步处理异常,在AsyncCompleted中你可以像这样调用你想要的Oops(exception e)方法:
void ClientAsyncOperationCompleted(object sender, AsyncOperationEventArgs e)
{
if (e.Error == null) {
//Normal execution path
}
else {
Oops(e.Error);
}
}
没有Rx,这很复杂,但很强大。