c#async lambda世界中的错误处理策略?

时间:2011-09-13 00:06:07

标签: c# silverlight asynchronous

我曾经编写过厚的c#客户端代码,会在机房中抛出异常(网络故障说),并期望有人在我之上处理它。最终冒泡到可以处理剩余的appwide try / catch(“foo栏中的意外异常,点击查看详细信息,联系支持......”),加上可能是appdomain wide unhandled exception handler以防万一。

现在我在Silverlight中写了一个“厚”的客户端,我不知道该怎么办了。大量的我的机房代码作为网络事件驱动的回调运行,然后我调用原始调用者提供给我的lambda;但他们早已不复存在。我不能向他们抛出异常,没有堆叠起泡。我不能强迫他们放松。它们仍处于挂起状态(无论对于特定的异步调用可能意味着什么)。

我看到AsyncEventCompletedArgs及其RaiseExceptionIfNeeded技巧。使用它似乎将我的低级管道暴露给更高级别的代码(想想MVVM,MVC,......)。我希望默认帮助上层代码“做正确的事”,就像它们在好的旧同步/异常驱动时所做的那样。但这没有帮助。他们从AsyncArgs实例读取结果,抛出并且回调消失,让他们仍然在逻辑上悬空

无论如何 - 希望这是有道理的。有没有人有任何设计,他们想分享的经验。任何SL MVVM,MVC框架都有什么作用?

注意 - 我不是在谈论c#5中花哨的新Async内容(这会有所帮助吗?)

(可能会因为不够具体,过于主观等而被关闭,叹息)

4 个答案:

答案 0 :(得分:2)

你可以让调用者为成功完成提供一个lambda,也可以为你的代码中捕获异常的情况提供一个例外的lambda。

Reactive Extensions在订阅observable时允许这样做。

有些事情:

asyncCall.Subscribe(result => DoSomething(result), ex => Oops(ex));

答案 1 :(得分:1)

如果您的要求包含重要的并行未完成的异步操作,那么不要再费心阅读此答案了。但是,如果按顺序调用这些操作(一个没有被调用,直到另一个完成),听起来可能是这样的,那么你可能会发现这里提到的东西有点用处。

处理Silverlight中的异步代码是我非常感兴趣的主题。我写过几篇关于我称之为AsynchOperationService的博客,它使代码能够使用简单的同步查找顺序样式编写,并且包含对错误处理的良好支持。

从这个list中挑选出来。

Rx可能是一个看起来很强大的选项。然而,它很复杂,最初设计用于处理传入事件流。我不相信(虽然我承认我的研究仍然很浅)它非常适合“开始一些事情,稍后回应那个完成”的操作。

就个人而言,我喜欢简单直接的事情,但很明显我需要完全改变我编码的方式来正确地包含Rx的功能,而且我相当肯定所得到的代码不会那么容易读取。

虽然AsyncOperationService有一个非常轻的足迹,而且一旦我创建了一些有用的单行函数,所得到的代码序列非常简单易读。

那么抓到了什么?那么有一个,它要么正是你需要的,要么完全符合你的需要。该方法的工作原理是假设需要按顺序完成一系列异步操作。如果您的要求是针对重要的并行操作,则以当前形式AsyncOperationService不适合您(尽管可能会更改)。

答案 2 :(得分:0)

我使用以下结构的代码来处理异常问题async:

        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的东西,这是相当复杂但功能强大的。

答案 3 :(得分:0)

诀窍是将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友好版本:

Simple ActionQueue in MetaSharp

我正在使用队列,因为它允许我保证在后台线程上完成工作的顺序。当你有一堆异步操作时,它很容易进入竞争条件等等,一个队列是一个很好的结构,可以保证它们的执行顺序,即使它们实际上都是异步的。 queueGroup用于您还希望并行和异步的操作。