2009-10-12 8 views
8

Kürzlich habe ich ein Interview besucht. Ein Code-Schnipsel wird mir gegeben. Ich weiß, der Interviewer hat es aus Albharis Threading-Probe genommen.Ausnahmebehandlung in Threads

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; } 

Die Modifikation des obigen Code als

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 
     ... 
    } 
} 

würde die guten Kandidaten sein, die Ausnahme zu behandeln.

Ich bin gebeten worden, „Mit Ausnahme der obigen Spur, was die anderen Alternativen sind, würden als gute Lösung passen ?. Es ist schwer war die Alternative zu finden, so dass ich hier heben sie Ihren Vorschlag zu sammeln.

Antwort

15

Exception geworfen gehen Sie und übergibt es an Haupt-Thread explizit in einem Thread könnte normalerweise nicht in einem anderen Thread gefangen werden.

ist besser, es in Funktion zu fangen.

wenn Sie jedoch nur alle nicht behandelte Nachrichten protokollieren mögen Von allen Threads können Sie AppDomain.UnhandledException Ereignis oder ähnliche Ereignisse in der Application-Klasse verwenden, wenn Sie dies tun WinForms- oder WPF-App entwickeln. passen würde als gute Lösung

+10

... aber darüber im Klaren sein, dass Sie nicht die Ausnahme in AppDomain.UnhandledException verarbeiten können, Sie benachrichtigen, aber die Anwendung wird sowieso abgeschaltet werden . – Lucero

+1

Es ist möglich, das Beenden der Anwendung zu verhindern, indem Sie den Kompatibilitätsmodus v1.x einstellen. Dazu muss ein -Element zu app.config im

3

, was die anderen Alternativen sind ?.

Lösung zu was? Welches Problem versuchen Sie zu lösen?

Wenn Sie BackgroundWorker verwenden, um Gewinde im Gegensatz, sie hat ein RunWorkerCompleted Ereignis, und dass innerhalb Sie die RunWorkerCompletedEventArgs param für die Eigenschaft Fehler können überprüfen. Dies wird normalerweise in WinForms- oder WPF-Anwendungen verwendet, da BackgroundWorker im Visual Studio-Designer gut unterstützt wird.

Sie könnten auch einen Delegaten für Go() definieren und BeginInvoke() darauf aufrufen. Natürlich brauchst du auch die EndInvoke().

Außerdem ist es generell keine gute Idee, zufällige Threads zu starten. ThreadPool.QueueUserWorkItem, BackgroundWorker oder asynch delegates verwenden den ThreadPool und werden empfohlen.

+0

zum selben Code hinzugefügt werden, nach dem sie Alternativen gefragt haben. – user184805

+0

Nun, was ich meine, ist dies: Der ursprüngliche Code wurde gebrochen. Ihre erste Option ist eine Alternative. Wenn es zufriedenstellend ist, dann brauchen Sie keine anderen Alternativen. Welches Problem versuchen Sie zu lösen? "Eine Alternative zum ursprünglichen Code" könnte * alles * sein. Sie könnten Code einreichen, der Sudoku-Rätsel löst - das wäre eine Alternative. Wäre es angemessen? Ohne Anforderungen, wer kann das sagen? – Cheeso

+0

Nein, Sir, der Interviewer hat diese Frage gestellt. :) also habe ich nichts erzählt. Außerdem wusste ich nicht, wie ich damit umgehen kann. :) Danke für Ihren Vorschlag. – user184805

0

Es gibt Alternativen aufgelistet auf Joe Albahari Webseite: http://www.albahari.com/threading/#_Exception_Handling

„Es gibt jedoch einige Fälle, in denen Sie nicht brauchen, Ausnahmen auf einem Worker-Thread zu handhaben, weil das .NET Framework macht es für Sie .Diese werden in den kommenden Abschnitten behandelt, und sind:
Asynchroner Delegierten
-BackgroundWorker
-Der Task Parallel Library (unter Bedingungen)“

0

Ich denke, das ist der einfachste Weg ist:

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(); 

aber es gibt eine andere Möglichkeit, die einige bevorzugen, wenn Sie den Thread synchronisieren möchten (vielleicht ist dies in einem anderen Thread als dem GUI-Thread):

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); 
    } 

Beachten Sie, dass das zweite Beispiel mehr Sinn machen würde, wenn Sie mehrere Threads haben und diese durchschleifen und allen beitreten ... aber ich behalte das Beispiel einfach.

das dritte Muster würde Task-Fabrik werden, das ist sauberer:

private static test(){ 
    List<Task<float>> tasks = new List<Task<float>>(); 
    for (float i = -3.0f; i <= 3.0f; i+=1.0f) 
    { 
     float num = i; 
     Console.WriteLine("sent " + i); 
     Task<float> task = Task.Factory.StartNew<float>(() => Div(5.0f, num)); 
     tasks.Add(task); 
    } 

    foreach(Task<float> 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; 
}