Advanced Java Services | Threads beenden |
Wie in Javca gibt es auch in C# in der Klasse Thread Methoden und Properties, die nicht mehr verwendet werden sollen, da sie nicht korrekt arbeiten. Außer den oben erwähnten Methoden findet man noch weitere in der API unter msdn.microsoft.com/en-us/library/system.threading.thread.aspx.
Thread die quasi endlos laufen sind nichts unübliches. Hier kann man eine Bedingung einbauen und prüfen, ob man die Schleife verlassen soll. Das wird mit dem folgenden Beispiel demonstriert.
class MyThread { private bool running = true; private Thread th; public MyThread() { this.th = new Thread(Run); this.th.Start(); } public void Run() { int i=0; while(running) { Console.WriteLine("runnin' " + ++i); // some work... } Console.WriteLine("end Run"); } public void stopThread() { running = false; } }
Ein Mainprogramm startet den Thread und beendet ihn nach ca. 0,1 Millisekunde. Mit Hilfe der Struktur TimeSpan lassen sich kleinere Zeiteinheiten realisieren. Die Kleinste Einheit ist 1 tick, was ca. 100 Nanosekunden entspricht, dementsprechend sind 1000 ticks etwa 0,1 Sekunden.
public class Main { public static void Main(String[] args) { TimeSpan ts = new TimeSpan(1000); // 1 tick = 100 nanoseconds, 10 000 ticks = 1 millisecond MyThread mt = new MyThread(); // Thread startet sofort Thread.Sleep(ts); // 0,1 milliseconds mt.stopThread(); } }
Bei dieser Variante wird der Thread immer nach einem vollendeten Schleifendurchlauf beendet. Drei Screenshots zeigen das deutlich unterschiedliche Verhalten.
Die Methode Interrupt() aus der Klasse Thread bietet eine weitere Möglichkeit einen Thread zu beenden.
Wie der Name schon sugeriert, unterbricht diese Methode den laufenden Thread lediglich, indem sie eine ThreadInterruptedException auslöst. Im catch-Zweig kann dann entschieden werden, ob man den Thread tatsächlich beenden will. Eine ThreadInterruptedException wird aber nur dann ausgelöst, wenn der Thread sich im Zustand WaitSleepJoin befindet.
Die Methode TestInterrupt() beendet bei der dritten Interruptanforderung die Endlosschleife mit einem break.
public static void TestInterrupt() { for (int i = 1; ; i++) { try { Console.WriteLine("Thread going to sleep."); Thread.Sleep(Timeout.Infinite); } catch (ThreadInterruptedException ex) { Console.WriteLine("sleep interrupted by another thread " + i); if (i == 3) break; } } Console.WriteLine("end thread"); }
Main kann z. Bsp. so aussehen.
private static void Main(string[] args) { Thread th = new Thread(TestInterrupt); th.Start(); Thread.Sleep(1000); // 1 second th.Interrupt(); Thread.Sleep(1000); // 1 second th.Interrupt(); Thread.Sleep(1000); // 1 second th.Interrupt(); Thread.Sleep(2000); th.Interrupt(); // Interrupt auf einen bereits beendeten Thread löst keine Exception aus. }
Die Konsolausgabe.
Ein nichtunterbrechbares Warten kann man mit der Methode SpinWait() erreichen. Diese Methode arbeitet mit einer Schleife ohne Statements, die nach einer Anzahl von Wiederholungen beendet wird. Die Anzahl wird der Methode als Parameter übergeben. Die folgende Methode TestSpinWait() beendet sich nach 100 Millionen Durchläufen und ignoriert Interruptanforderungen.
public static void TestSpinWait() { try { Console.WriteLine("Thread going to spinwait."); Thread.SpinWait(1000000000); Console.WriteLine("end spinwait"); } catch (ThreadInterruptedException ex) { Console.WriteLine("interrupted by another thread"); } }
Main
private static void Main(string[] args) { Thread th = new Thread(TestSpinWait); th.Start(); Thread.Sleep(10); // 10 millisecond th.Interrupt(); Console.WriteLine("end main");
Die Meldung aus dem catch()-Zweig wird nicht ausgegeben.
Es geht auch ohne SpinWait(). Die folgende rechenintensive (aber nutzlose) Schleife ist nicht mit Interrupt() unterbrechbar, es sei denn man fügt ein Thread.Sleep() ein, wobei ein tick (100 Nanosekunden) schon ausreicht.
public static void InterruptTest()
{
TimeSpan ts = new TimeSpan(1); // 100 nanosek
for(int j = 1 ; ; j++)
{
try
{
Console.WriteLine("runnin' " + j);
long sum = 0;
for(long i=1; i<100000; i++)
{
sum += i*i*i*i*i/i/i/i/i;
}
Thread.Sleep(ts); // (*)
}
catch(ThreadInterruptedException ex)
{
Console.WriteLine("interrupted by another thread");
break;
}
} // end for
Console.WriteLine("end Run");
}
Das zugehörige Main versucht nach 100 Millisekunden eine Unterbrechung.
private static void Main(string[] args) { Thread th = new Thread(InterruptTest); th.Start(); Thread.Sleep(100); // 100 millisecond th.Interrupt(); Console.WriteLine("end main"); }
Falls in der Methode InterruptTest() das mit (*) gekennzeichnete Statement Thread.Sleep(1) nicht auskommentiert ist sieht die Konsolausgabe so aus, ...
und falls man es auskommentiert (und dann nach etwa fünf Sekunden den Screenshot macht), dann so.