【问题标题】:Catching an exception thrown in an asynchronous callback捕获异步回调中抛出的异常
【发布时间】:2012-02-03 15:21:37
【问题描述】:

我有一个采用回调参数异步执行的方法,但 catch 块似乎没有捕获同步调用引发的任何异常(this.Submit 指的是同步方法)。

public void Submit(FileInfo file, AnswerHandler callback)
{
    SubmitFileDelegate submitDelegate = new SubmitFileDelegate(this.Submit);
    submitDelegate.BeginInvoke(file, (IAsyncResult ar) =>
    {
        string result = submitDelegate.EndInvoke(ar);
        callback(result);
    }, null);
}

有没有办法捕获新线程抛出的异常并将其发送到原始线程?另外,这是处理异步异常的“正确”方式吗?我编写了我的代码,因此可以这样调用它(假设异常问题已修复):

try
{
    target.Submit(file, (response) =>
    {
        // do stuff
    });
}
catch (Exception ex)
{
    // catch stuff
}

但是有没有更合适或更优雅的方法来做到这一点?

【问题讨论】:

  • 第一个代码示例中的 catch 块将捕获由回调或 EndInvoke 引发的异常。第二个代码示例中的 catch 块将捕获由 SubmitFileDelegate 构造函数或 BeginInvoke 引发的任何异常。哪一个没有做你期望/希望它做的事情?
  • 糟糕,忘记删除了。我希望第二个能正常工作,但目前都没有。
  • 你能说明SubmitFileDelegate是如何定义的吗?
  • 只是一个非常通用的private delegate string SubmitFileDelegate(FileInfo file);

标签: c# asynchronous lambda callback


【解决方案1】:

简而言之,没有。

当您调用 submitDelegate.BeginInvoke 时,它会生成新线程、返回并立即退出您的 try/catch 块(而新线程在后台运行)。

但是,您可以像这样捕获 所有 未处理的异常:

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(YourException);

但是,这将捕获应用程序域中的所有内容(不仅仅是您的异步​​方法)。

【讨论】:

  • 那么有没有首选的方法来处理异常?我习惯于让回调类似于 JS 世界中的 function(err, result) { } 之类的东西,但是在我浏览过的 C# 代码示例中我没有看到类似的东西,所以我不确定这是否是这样它应该在 C# 中完成... :S
  • @w0lf 的回答基本上是这样的;您将响应类型中发生的异常包装起来,并在完成后将其拉出。
【解决方案2】:

如果您的目标是 .NET 4.0,则可以利用新的任务并行库,并观察 Task 对象的 Exception 属性。

public Task Submit(FileInfo file)
{
    return Task.Factory.StartNew(() => DoSomething(file));
}

private void DoSomething(FileInfo file)
{
    throw new Exception();
}

然后像这样使用它:

Submit(myFileInfo).ContinueWith(task =>
{
    // Check task.Exception for any exceptions.

    // Do stuff with task.Result
});

其中DoSomething 是您希望异步调用的方法,而您传递给ContinueWith 的委托是您的回调。

更多关于 TPL 中异常处理的信息可以在这里找到:http://msdn.microsoft.com/en-us/library/dd997415.aspx

【讨论】:

    【解决方案3】:

    这不是一个“最佳实践”解决方案,但我认为它应该是一个简单的解决方案。

    而不是将委托定义为

    private delegate string SubmitFileDelegate(FileInfo file);
    

    定义为

    private delegate SubmitFileResult SubmitFileDelegate(FileInfo file);
    

    并定义 SubmitFileResult 如下:

    public class SubmitFileResult
    {
        public string Result;
        public Exception Exception;
    }
    

    那么,实际提交文件的方法(问题中没有显示)应该这样定义:

    private static SubmitFileResult Submit(FileInfo file)
    {
        try
        {
            var submissionResult = ComplexSubmitFileMethod();
    
            return new SubmitFileResult { Result = submissionResult };
        }
        catch (Exception ex)
        {
            return new SubmitFileResult {Exception = ex, Result = "ERROR"};
        }
    }
    

    这样,您将检查结果对象,查看它是否设置了 Result 或 Exception 字段,并采取相应措施。

    【讨论】:

    • 这个调用不是同步的吗?我尝试在 BeginInvoke 回调中放置一个 try/catch(并遵循您的示例),但它似乎没有捕捉到异常。
    • 不,它不是同步的。请注意有两种Submit 方法:我上面描述的一种(必须与SubmitFileDelegate 定义匹配)和您在问题中提出的一种BeginInvokes 另一种(这意味着它是一个异步调用)。
    猜你喜欢
    • 2011-04-10
    • 2013-06-24
    • 2016-02-17
    • 1970-01-01
    • 2011-06-27
    • 2020-01-28
    • 2017-10-14
    • 1970-01-01
    相关资源
    最近更新 更多