In Multithreading Anwendungen stellt sich immer wieder die Frage wie das Exception Handling der einzelnen Threads erfolgen soll. Was geschieht wenn in einem ein Thread eine Exception geworfen wird? Wie lässt sich diese abfangen? Wird bei einer unbehandelten Exception nur der Thread oder die komplette Anwendung beenden?
Lassen Sie mich die Fragen mittels eines kleinen Testprogramms beantworten. Der nachfolgende Quellcode zeigt den Aufruf einer Funktion in welcher eine Division durch null durchgeführt wird. Der Funktionsaufruf erfolgt in einem separaten Thread. Der Thread wird dabei innerhalb eines try-catch Blocks gestartet.
1 static void Main(string[] args)
2 {
3 try
4 {
5 new Thread(() => Calculate(0)).Start();
6 }
7 catch (DivideByZeroException)
8 {
9 //error handling
10 }
11 }
12
13 private static int Calculate(int value)
14 {
15 return 5 / value;
16 }
Die Ausführung dieser Quellcodes resultiert in einem Absturz der Anwendung. Die Exception wird innerhalb des Threads geworfen und lässt sich daher nicht ausserhalb in dem try-catch Block behandeln. Dies führt zum Absturz der Applikation.
Um den Fehler im Thread zu behandeln muss der try-catch Block in die Hauptmethode des Threads verschoben werden. Der nachfolgende Quellcode zeigt die angepasste Applikation.
1 static void Main(string[] args)
2 {
3 new Thread(() => Calculate(0)).Start();
4 }
5
6 private static int Calculate(int value)
7 {
8 try
9 {
10 return 5 / value;
11 }
12 catch (DivideByZeroException)
13 {
14 //error handling
15 return 0;
16 }
17 }
Die Exception wird nun direkt im Thread behandelt. Dadurch kann die Applikation ausgeführt werden ohne dass sie abstürzt. Oftmals soll die Fehlerbehandlung aber im Hauptthread erfolgen. In solch einem Szenario muss die Exception im parallelen Thread gefangen und an den Hauptthread weitergegeben werden. Im nachfolgenden Beispiel wurde dies unter Zuhilfenahme einer lokalen Variable realisiert.
1 static void Main(string[] args)
2 {
3 Exception threadException = null;
4
5 Thread workerThread = new Thread(() =>
6 {
7 try
8 {
9 Calculate(0);
10 }
11 catch (DivideByZeroException ex)
12 {
13 threadException = ex;
14 }
15 });
16
17 workerThread.Start();
18
19 workerThread.Join();
20 if (threadException != null)
21 {
22 //error handling or throw exception on main thread
23 }
24 }
25
26 private static int Calculate(int value)
27 {
28 return 5 / value;
29 }
Anhand dieses Beispiels sehen Sie, dass es nur wenig Code bedarf um die Exception zu fangen und an den Hauptthread weiter zu geben. Noch einfacher gestaltet sich die Fehlerbehandlung wenn statt der Thread Klasse die BackgoundWorker oder die Task Klasse zum Einsatz kommt. Diese Klassen bieten bereits Mechanismen zur Fehlerbehandlung. Mehr zu diesen beiden Klassen finden Sie in den anderen Artikeln dieser Artikelserie.
Fazit
Exceptions, welche in parallelen Threads geworfen werden, müssen in den jeweiligen Threads behandelt werden. Unbehandelte Exceptions führen ansonsten zum Absturz der Applikation. Die Thread Klasse bietet keine direkte Unterstützung für die Fehlerbehandlung. Mit relativ wenig Aufwand lässt sich aber eine entsprechende Lösung implementieren.