Gewusst wie: Erstellen und Beenden von Threads (C#-Programmierhandbuch)

Aktualisiert: November 2007

In diesem Beispiel wird veranschaulicht, wie ein Hilfs- bzw. Arbeitsthread erstellt und für die Verarbeitung parallel zum primären Thread verwendet werden kann. Darüber hinaus wird veranschaulicht, wie Sie einen Thread auf einen anderen warten lassen und wie ein Thread ordnungsgemäß beendet wird. Hintergrundinformationen zum Multithreading finden Sie unter Verwaltetes Threading und Verwenden von Threading (C#-Programmierhandbuch)

Im Beispiel wird eine Klasse mit dem Namen Worker erstellt. Diese enthält die DoWork-Methode, die vom Arbeitsthread ausgeführt wird. Im Prinzip handelt es sich hierbei um die Main-Funktion für den Arbeitsthread. Die Ausführung des Arbeitsthreads wird gestartet, indem diese Methode aufgerufen wird, und der Thread wird automatisch beendet, wenn diese Methode einen Wert zurückgibt. Die DoWork-Methode sieht wie folgt aus:

public void DoWork()
{
    while (!_shouldStop)
    {
        Console.WriteLine("worker thread: working...");
    }
    Console.WriteLine("worker thread: terminating gracefully.");
}

Die Worker-Klasse enthält eine zusätzliche Methode, die verwendet wird, um DoWork anzuzeigen, dass ein Wert zurückgegeben werden muss. Hierbei handelt es sich um die RequestStop-Methode, die folgendermaßen aussieht:

public void RequestStop()
{
    _shouldStop = true;
}

Die RequestStop-Methode weist true einfach nur den _shouldStop-Datenmember zu. Da dieser Datenmember von der DoWork-Methode überprüft wird, hat dies indirekt zur Folge, dass DoWork einen Wert zurückgibt, wodurch der Arbeitsthread beendet wird. Es ist jedoch zu beachten, dass DoWork und RequestStop von verschiedenen Threads ausgeführt werden. DoWork wird vom Arbeitsthread ausgeführt, und RequestStop wird vom primären Thread ausgeführt. Daher wird der _shouldStop -Datenmember wie folgt als volatile deklariert:

private volatile bool _shouldStop;

Das volatile-Schlüsselwort warnt den Compiler, dass mehrere Threads auf den _shouldStop-Datenmember zugreifen, weswegen keine Optimierungsannahmen über den Zustand dieses Members angestellt werden dürfen. Weitere Informationen finden Sie unter volatile (C#-Referenz).

Durch die Verwendung von volatile mit dem _shouldStop-Datenmember ist es möglich, ohne formale Threadsynchronisierungsverfahren von mehreren Threads aus sicher auf diesen Member zuzugreifen. Dies ist allerdings nur der Fall, weil _shouldStop ein Wert vom Typ bool ist. Das bedeutet, dass zum Ändern von _shouldStop nur einzelne, atomare Operationen notwendig sind. Wenn es sich hingegen bei diesem Datenmember um eine Klasse, eine Struktur oder einen Array handeln würde, würde ein Zugriff von mehreren Threads vermutlich zu regelmäßig auftretenden Datenbeschädigungen führen. Betrachten Sie einen Thread, der die Werte in einem Array ändert. Windows unterbricht Threads in regelmäßigen Abständen, um die Ausführung anderer Threads zu ermöglichen. Dieser Thread könnte demnach zu einem Zeitpunkt angehalten werden, zu dem einige Arrayelemente zugewiesen sind, andere jedoch nicht. Das Array befindet sich nun in einem Zustand, der vom Programmierer niemals beabsichtigt war. Eine mögliche Folge ist ein Fehler in einem anderen Thread beim Lesen dieses Arrays.

Bevor die Main-Funktion den Arbeitsthread tatsächlich erstellt, erstellt sie ein Worker-Objekt und eine Instanz von Thread. Das Threadobjekt wird so konfiguriert, dass die Worker.DoWork-Methode als Einstiegspunkt verwendet wird, indem wie folgt ein Verweis auf diese Methode an den Thread-Konstruktor übergeben wird:

Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);

Obwohl das Arbeitsthreadobjekt an diesem Punkt bereits vorhanden und konfiguriert ist, wurde der eigentliche Arbeitsthread noch nicht erstellt. Dies geschieht erst, wenn Main die Start-Methode aufruft:

workerThread.Start();

Daraufhin initialisiert das System die Ausführung des Arbeitsthreads, die im Verhältnis zum primären Thread allerdings asynchron ist. Das bedeutet, dass die Main-Funktion die sofortige Ausführung von Code fortsetzt, während der Arbeitsthread gleichzeitig initialisiert wird. Um sicherzustellen, dass die Main-Funktion nicht versucht, den Arbeitsthread zu beenden, bevor er ausgeführt werden kann, durchläuft die Main-Funktion solange Schleifen, bis die IsAlive-Eigenschaft des Arbeitsthreadobjekts auf true festgelegt wird:

while (!workerThread.IsAlive);

Danach wird der primäre Thread kurz mit einem Aufruf von Sleep angehalten. Auf diese Weise wird sichergestellt, dass die DoWork-Funktion des Arbeitsthreads einige Iterationen der Schleife innerhalb der DoWork-Methode ausführt, bevor die Main-Funktion weitere Befehle ausführt:

Thread.Sleep(1);

Nach einer Millisekunde signalisiert Main dem Arbeitsthreadobjekt, dass es mit der zuvor eingeführten Worker.RequestStop-Methode beendet wird:

workerObject.RequestStop();

Sie können einen Thread auch mit einem Aufruf von Abort von einem anderen Thread aus beenden. Allerdings wird hierbei die Beendigung des betroffenen Threads erzwungen, auch wenn seine Aufgabe noch nicht abgeschlossen ist. Außerdem besteht keine Möglichkeit zum Bereinigen der Ressourcen. Das in diesem Beispiel angewendete Verfahren ist daher vorzuziehen.

Schließlich ruft die Main-Funktion die Join-Methode für das Arbeitsthreadobjekt auf. Diese Methode sorgt dafür, dass der aktuelle Thread blockiert wird bzw. darauf wartet, dass der Thread, der das Objekt darstellt, beendet wird. Deshalb gibt Join keinen Wert zurück, bis der Arbeitsthread einen Wert zurückgibt und sich dadurch selbst beendet:

workerThread.Join();

Zu diesem Zeitpunkt ist nur noch der primäre Thread, der Main ausführt, vorhanden. Dieser zeigt eine abschließende Meldung an und gibt dann einen Wert zurück, wodurch der primäre Thread ebenfalls beendet wird.

Im Folgenden sehen Sie das vollständige Beispiel.

Beispiel

using System;
using System.Threading;

public class Worker
{
    // This method will be called when the thread is started.
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("worker thread: working...");
        }
        Console.WriteLine("worker thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Volatile is used as hint to the compiler that this data
    // member will be accessed by multiple threads.
    private volatile bool _shouldStop;
}

public class WorkerThreadExample
{
    static void Main()
    {
        // Create the thread object. This does not start the thread.
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);

        // Start the worker thread.
        workerThread.Start();
        Console.WriteLine("main thread: Starting worker thread...");

        // Loop until worker thread activates.
        while (!workerThread.IsAlive);

        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work:
        Thread.Sleep(1);

        // Request that the worker thread stop itself:
        workerObject.RequestStop();

        // Use the Join method to block the current thread 
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("main thread: Worker thread has terminated.");
    }
}

Die Ausgabe lautet wie folgt:

main thread: starting worker thread...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: working...
worker thread: terminating gracefully...
main thread: worker thread has terminated

Siehe auch

Aufgaben

Beispiel für Threading

Konzepte

C#-Programmierhandbuch

Referenz

Threading (C#-Programmierhandbuch)

Verwenden von Threading (C#-Programmierhandbuch)

Thread

volatile (C#-Referenz)

Mutex

Monitor

Start

IsAlive

Sleep

Join

Abort

Weitere Ressourcen

Verwaltetes Threading

Threadbeispiele