2013-07-26 124 views
5

我有一个工作线程,当它终止时,表示一个事件。然后将此事件编组到主线程以通知工作线程的终止。当工作线程遇到未处理的异常时,我希望这个异常由主线程的错误处理系统来处理。因此,工作线程设置一个属性来指示其意外终止,并将异常保存在另一个属性中,然后发出事件并退出。当线程意外退出时抛出什么异常?

将事件整理到主线程后,我想抛出一个新异常,并将原始异常设置为内部异常。我的问题是:这个新Exception的类型应该是什么?对于这种情况是否存在特定的System.somethingException,是否应该为这种特定情况设计我自己的Exception类,或者是否会抛出一个标准的System.Exception和适当的消息?

C#-psuedo代码:

class MyThread 
{ 
    public TerminationState Termination { get; private set; } 
    public Exception UncaughtException { get; private set; } 

    public delegate void ThreadTerminatedDelegate(MyThread thread); 
    public event ThreadTerminatedDelegate ThreadTerminated; 

    private void run() 
    { 
     try 
     { 
      doSomeWork(); 
     } 
     catch(Exception e) 
     { 
      UncaughtException = e; 
      Termination = TerminationState.AbortOnException; 
      ThreadTerminated(this); 
      return; 
     } 
     Termination = TerminationState.NormalTermination; 
     ThreadTerminated(this); 
    } 
} 

class MainThread 
{ 
    private MyThread myThread = new MyThread(); 

    private void run() 
    { 
     myThread.ThreadTerminated += handleTermination; 
     myThread.Start(); 
    } 

    private void handleTermination(MyThread thread) 
    { 
     if (InvokeRequired) 
     { 
      MyThread.ThreadTerminatedDelegate cb = new MyThread.ThreadTerminatedDelegate(handleTermination); 
      BeginInvoke(cb, new object[] { thread }); 
     } 
     else 
     { 
      if (thread.Termination == TerminationState.AbortOnException) 
      { 
       if (isFatal(thread.UncaughtException)) 
        throw new Exception("", thread.UncaughtException); //what to do here? 
       else 
        fixTheProblem(); 
      } 
      else 
      { 
       //normal wrapping up 
      } 
     } 
    } 
} 
+0

我想说这取决于线程在做什么。实际上,我不喜欢AggregateException,它将所有东西都平铺到一个通用的错误上,以便吞下原来的其他东西的异常......看起来不是那么好的主意。我会克隆它(保持原始堆栈跟踪),我会重新抛出它。但这只是我的意见... –

+0

当你再次抛出异常时,你希望发生什么? 99%的可能性是你的程序会崩溃。这是一件好事,有些事情没有完成,你也没有希望完成。但是,那就是,首先不要去理解这个例外。它也会崩溃,但至少你会得到更好的诊断,不必编写无用的代码。 –

+0

@Adriano而不是克隆和rethrowing,你不能只使用'throw;'?这保持了原始的堆栈跟踪,对吧? – JSQuareD

回答

1

我相信,你可以通过在Task进行后台工作,然后在该任务的继续处理任何例外执行所有必要的异常处理未处理的背景例外明确安排在主线程上运行。您可以为延续指定其他选项,但这应该涵盖您的方案。

Task.Factory.StartNew(
    () => 
    { 
     // Do some work that may throw. 
     // This code runs on the Threadpool. 
     // Any exceptions will be propagated 
     // to continuation tasks and awaiters 
     // for observation. 
     throw new StackOverflowException(); // :) 
    } 
).ContinueWith(
    (a) => 
    { 
     // Handle your exception here. 
     // This code runs on the thread 
     // that started the worker task. 
     if (a.Exception != null) 
     { 
      foreach (var ex in a.Exception.InnerExceptions) 
      { 
       // Try to handle or throw. 
      } 
     } 
    }, 
    CancellationToken.None, 
    TaskContinuationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext() 
); 

另一个有用的链接是MSDN's Asyncronous Programming Patterns。它确定了在应用程序中实现异步操作的三种主要方式。您当前的实现听起来与本文称为EAP(基于事件的异步模式)类似。

我个人比较喜欢依赖于.NET 4.0 TPL(任务并行库)的TAP(基于任务的异步模式)。由于其语法的简单性和广泛的功能,它非常值得掌握。

从MSDN:

  • 异步编程模型(APM)图案(也称为IAsyncResult的图案),其中异步操作需要开始和结束的方法(例如,BeginWrite和EndWrite异步写操作)。这种模式不再推荐用于新的开发。有关更多信息,请参阅异步编程模型(APM)。
  • 基于事件的异步模式(EAP),它需要一个具有Async后缀的方法,并且还需要一个或多个事件,事件处理程序委托类型和EventArg派生类型。 EAP是在.NET Framework 2.0中引入的。它不再被推荐用于新的开发。有关更多信息,请参阅基于事件的异步模式(EAP)。
  • 基于任务的异步模式(TAP),它使用单一方法来表示异步操作的启动和完成。 TAP是在.NET Framework 4中引入的,并且是.NET Framework中推荐的异步编程方法。有关更多信息,请参阅基于任务的异步模式(TAP)。

此外,不要忘记可信赖的BackgroundWorker类。这门课很长一段时间对我来说很重要,尽管它已经被TAP所使用,但它仍然可以完成工作,而且很容易理解和使用。

// Create a new background worker. 
var bgw = new BackgroundWorker(); 

// Assign a delegate to perform the background work. 
bgw.DoWork += (s, e) => 
    { 
     // Runs in background thread. Unhandled exceptions 
     // will cause the thread to terminate immediately. 
     throw new StackOverflowException(); 
    }; 

// Assign a delegate to perform any cleanup/error handling/UI updating. 
bgw.RunWorkerCompleted += (s, e) => 
    { 
     // Runs in UI thread. Any unhandled exception that 
     // occur in the background thread will be accessible 
     // in the event arguments Error property. 
     if (e.Error != null) 
     { 
      // Handle or rethrow. 
     } 
    }; 

// Start the background worker asynchronously. 
bgw.RunWorkerAsync(); 
+0

感谢您的回答!现在我正在度假,所以我没有时间去看它。当我下周回来的时候,我会详细地看看它,如果它解决了问题,请接受答案。 :) – JSQuareD

+1

是的,这是我的丑陋黑客的优雅替代品,谢谢!另外,感谢对不同的异步编程模式的监督,我已经开始阅读“简单的C#”,并且可能会在未来的时间内通过这些工作。 :) – JSQuareD

相关问题