线程中的exception处理

最近我参加了一次采访。 给我一个代码片段。我知道,面试官是从albhari的线程样本中获取的。

public static void Main() { try { new Thread (Go).Start(); } catch (Exception ex) { // We'll never get here! Console.WriteLine ("Exception!"); } } static void Go() { throw null; } 

将上述代码修改为

 public static void Main() { new Thread (Go).Start(); } static void Go() { try { ... throw null; // this exception will get caught below ... } catch (Exception ex) { Typically log the exception, and/or signal another thread that we've come unstuck ... } } 

将是处理exception的好候选人。

我被问到,“除了上面的描述,其他替代方案是否适合作为一个好的解决方案?。很难找到替代方案,所以我在这里提出来收集你的建议。

通常无法在另一个线程中捕获在线程中抛出的exception。

你最好在函数Go中捕获它并明确地将它传递给主线程。

但是,如果您只想记录来自所有线程的所有未处理消息,则可以在Application类中使用AppDomain.UnhandledException事件或等效事件(如果您正在开发WinForms或WPF应用程序)。

什么是其他替代品适合作为良好的解决方案?

解决方案是什么? 你想解决什么问题?

如果您使用BackgroundWorker而不是Thread,则它具有RunWorkerCompleted事件,在其中您可以检查RunWorkerCompletedEventArgs参数以获取Error属性。 这通常用于WinForms或WPF应用程序,因为Visual Studio设计器中对BackgroundWorker有很好的支持。

您还可以为Go()定义委托,并在其上调用BeginInvoke()。 当然你也需要EndInvoke()。

此外,启动随机线程通常不是一个好主意。 ThreadPool.QueueUserWorkItem,BackgroundWorker或asynch委托都使用ThreadPool,建议使用。

您可以使用AppDomain.UnhandledException事件

Joe Albahari的网站上列出了其他选择: http : //www.albahari.com/threading/#_Exception_Handling

“但是,在某些情况下,您不需要处理工作线程上的exception,因为.NET Framework会为您执行此操作。这些将在后续章节中介绍,并且是:
– 异步委托
-BackgroundWorker
– 任务并行库(条件适用)“

我认为这是最简单的方法:

 BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += new DoWorkEventHandler((object sender2, DoWorkEventArgs e2) => { throw new Exception("something bad"); e2.Result = 1 + 1; }); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((object sender2, RunWorkerCompletedEventArgs e2) => { if (e2.Error != null) { Console.WriteLine("Error: " + e2.Error.Message); } }); bw.RunWorkerAsync(); 

但是如果你想要同步线程(也许这是在GUI线程以外的线程上),有些人可能更喜欢这种方式:

  private class FileCopier { public bool failed = false; public Exception ex = null; public string localPath; public string dstPath; public FileCopier(string localPath, string dstPath) { this.localPath = localPath; this.dstPath = dstPath; } public void Copy() { try{ throw new Exception("bad path"); }catch(Exception ex2) { ex = ex2; failed = true; } } } public static void Main() { FileCopier fc = new FileCopier("some path", "some path"); Thread t = new Thread(fc.Copy); t.Start(); t.Join(); if (fc.failed) Console.WriteLine(fc.ex.Message); } 

请注意,如果您有多个线程并且循环遍历它们并加入所有线程,那么第二个示例将更有意义…但我保持示例简单。

第三种模式将使用更干净的Task Factory:

 private static test(){ List> tasks = new List>(); for (float i = -3.0f; i <= 3.0f; i+=1.0f) { float num = i; Console.WriteLine("sent " + i); Task task = Task.Factory.StartNew(() => Div(5.0f, num)); tasks.Add(task); } foreach(Task t in tasks) { try { t.Wait(); if (t.IsFaulted) { Console.WriteLine("Something went wrong: " + t.Exception.Message); } else { Console.WriteLine("result: " + t.Result); } }catch(Exception ex) { Console.WriteLine("Error: " + ex.Message); } } } private static float Div(float a, float b) { Console.WriteLine("got " + b); if (b == 0) throw new Exception("Divide by zero"); return a / b; }