Verwenden von Threads

Veröffentlicht: 06. Jul 2002 | Aktualisiert: 21. Jun 2004

Von Greg Ewing

In diesem Artikel werden die unterschiedlichen Threadingmodelle (Single-, Apartment- und Free-Threading) sowie die Verwendungsmöglichkeiten der einzelnen Modelle erläutert. Darüber hinaus wird ein Beispielprogramm in C# vorgestellt, das Threading verwendet. Es soll Ihnen das Schreiben von Anwendungen erleichtern, die Threading nutzen. Zudem geht der Artikel auf mögliche Probleme bei Multithreading ein.

Auf dieser Seite

Einführung Einführung
Hintergrundinformationen zu Threading Hintergrundinformationen zu Threading
Beispielanwendung Beispielanwendung
Potenzielle Probleme bei Multithreadingcode Potenzielle Probleme bei Multithreadingcode
Schlussfolgerung Schlussfolgerung

Beachten Sie, dass die Entwicklerkommentare in den Beispielprogrammdateien in Englisch verfasst sind, im Rahmen dieses Artikels jedoch zum besseren Verständnis übersetzt wurden.

Downloaden Sie die Beispieldatei.

Einführung

Eine Multithreading-MSMQ-Triggeranwendung (Microsoft® Message Queuing) zu schreiben, war von jeher eine abschreckende Aufgabe. Die Einführung von .NET-Framework-Threading und -Messagingklassen macht sie jedoch erheblich einfacher. Mit Hilfe dieser Klassen können Sie Multithreading-Anwendungen in jeder Sprache schreiben, die mit dem .NET Framework kompatibel ist.

Zuvor boten Tools wie Microsoft Visual Basic® nur sehr eingeschränkten Threadingsupport. Folglich waren Sie gezwungen, C++ für das Schreiben von Multithreadingcode zu verwenden, eine Lösung mit mehreren Prozessen oder ActiveX-DLLs in Visual Basic zu erstellen (nicht gerade ideal) oder ganz auf Multithreading zu verzichten. Mit dem .NET Framework können Sie nun unabhängig von der gewählten Sprache komplexe Multithreadinganwendungen erstellen.
In diesem Artikel werden Sie Schritt für Schritt durch die Erstellung einer Multithreadinganwendung geleitet, die auf Meldungen aus einer Microsoft-Meldungswarteschlange wartet und diese verarbeitet. Den Schwerpunkt dieses Artikels bilden zwei Namespaces: System.Threading und System.Messaging. Der Beispielcode ist zwar in C# verfasst, kann jedoch ganz einfach in eine Sprache Ihrer Wahl konvertiert werden.

 

Hintergrundinformationen zu Threading

Die drei grundlegenden Threadingmodelle in der Win32-Umgebung lauten wie folgt: Single-, Apartment- und Free-Threading.

Single-Threading
Einige Ihrer ersten selbst erstellten Anwendungen waren vermutlich Single-Thread-Anwendungen, wobei der einzelne Thread dem Anwendungsprozess entsprach. Ein Prozess kann als die Instanz einer Anwendung definiert werden, deren Eigentum der Speicherplatz dieser Anwendung ist. Die meisten Windows-Anwendungen sind Single-Thread-Anwendungen, und ein einzelner Thread übernimmt die gesamte Arbeit.

Apartment-Threading
Apartment-Threading ist die nächsthöhere Komplexitätsstufe innerhalb der Threadingmodelle. Code, der für Apartment-Threading eingerichtet ist, kann auf in seinem eigenen Thread ausgeführt werden und ist auf sein eigenes Apartment beschränkt. Ein Thread kann definiert werden als eine Entität, die einem Prozess gehört, dem Verarbeitungszeit zugewiesen wird. Im Apartment-Threading-Modell operieren alle Threads innerhalb ihrer jeweiligen Unterbereiche des Hauptanwendungsspeichers. Dieses Modell ermöglicht die simultane, aber unabhängige Ausführung mehrerer Codeinstanzen. Vor der Einführung von .NET war Visual Basic auf die Erstellung von Apartment-Threading-Komponenten und -Anwendungen beschränkt.

Free-Threading
Free-Threading ist das komplexeste Threadingmodell. Im Free-Threading-Modell können mehrere Threads gleichzeitig dieselben Methoden und Komponenten aufrufen. Anders als beim Apartment-Threading sind sie dabei nicht auf separate Speicherbereiche beschränkt. Ein Beispiel für die Verwendung eines Free-Threading-Objekts wäre, wenn eine Anwendung eine große Anzahl von sehr ähnlichen, aber voneinander unabhängigen mathematischen Berechnungen durchführen muss. In diesem Fall würden Sie mehrere Threads starten, um die Berechnungen unter Verwendung derselben Codeinstanz durchzuführen. C++-Entwickler sind vermutlich die einzigen Anwendungsentwickler, die bereits Free-Threading-Anwendungen geschrieben haben, denn in Sprachen wie Visual Basic 6.0 war dies nahezu unmöglich.

Arbeiten mit Threadingmodellen
Eine Möglichkeit, sich die Threadingmodelle vorzustellen, wäre, sie mit einem Umzug zu vergleichen. Wenn Sie einen Single-Threading-Ansatz wählen, erledigen Sie die gesamte Arbeit allein, vom Packen und Tragen der Kisten bis hin zum Auspacken. Wenn Sie ein Apartment-Threading-Modell wählen, bitten Sie einige gute Freunde um Hilfe. Jeder Freund arbeitet in einem anderen Zimmer und kann den Leuten in den anderen Räumen nicht helfen. Die einzelnen Helfer sind jeweils verantwortlich für den ihnen zugewiesenen Bereich und das entsprechende Verpackungsmaterial. Wenn Sie sich für einen Free-Threading-Ansatz entscheiden, bitten Sie zwar dieselben Freunde um ihre Hilfe, aber alle Helfer arbeiten gleichzeitig in einem der Zimmer und packen gemeinsam ein. In diesem Vergleich ist Ihr Haus der Prozess, in dem alle Threads ausgeführt werden, jeder Ihrer Freunde ist eine Instanz des Codes, und das Umzugsgut stellt die Ressourcen und Variablen Ihrer Anwendung dar.

Dieses Beispiel veranschaulicht einige der Vor- und Nachteile der verschiedenen Modelle. Apartment-Threading ist schneller als Single-Threading, da mehrere Instanzen Ihrer Komponente die Arbeit ausführen. Free-Threading ist in bestimmten Situationen schneller und effizienter als Apartment-Threading, da alles gleichzeitig geschieht und die verfügbaren Mittel gemeinsam genutzt werden. Dies kann jedoch zu Problemen führen, wenn mehrere Threads die gleichen Ressourcen für unterschiedliche Zwecke nutzen. Stellen Sie sich vor, ein Helfer füllt eine Kiste mit Küchengeräten, und ein anderer Helfer verwendet dieselbe Kiste für Badezimmerutensilien. Der erste Freund versieht die Kiste mit der Beschriftung "Küche", der zweite überschreibt dieses Etikett mit der Beschriftung "Bad". Beim Auspacken landen dann Ihre Küchengeräte im Badezimmer.

 

Beispielanwendung

Der erste Schritt besteht darin, dass wir uns das Design der Beispielanwendung genauer ansehen. Die Anwendung erstellt mehrere Threads, die jeweils auf Meldungen in einer MSMQ-Warteschlage warten. In diesem Beispiel werden zwei Klassen verwendet, die Form-Hauptklasse und eine benutzerdefinierte MQListen-Klasse. Die Form-Klasse verarbeitet die Benutzeroberfläche und erstellt, verwaltet und löscht die Arbeitsthreads. DieMQListen-Klasse enthält den gesamten Code (einschließlich Meldungswarteschlagenaspekte), der für das Ausführen der Arbeitsthreads erforderlich ist.

Vorbereiten der Anwendung

  • Zum Starten der Anwendung öffnen Sie Visual Studio .NET und erstellen eine neue C#-Windows-Anwendung mit dem Namen MultiThreadedMQListener. Öffnen Sie die Eigenschaften für das Formular, und geben Sie ihm den Namen QueueListenerForm. Sobald das Anfangsformular entworfen ist, ziehen Sie zwei Labels, zwei Schaltflächen, eine Statusleiste und zwei Textfelder darauf ab. Wählen Sie für das erste Textfeld den Namen Server und für das zweite den Namen Queue. Geben Sie der ersten Schaltfläche den Namen StartListening und der zweiten den Namen StopListening. Sie können den Standardnamen der Statusleiste, statusBar1, beibehalten.

  • Anschließend fügen Sie einen Verweis auf den System.Messaging-Namespace hinzu, indem Sie auf das Menü Projekt klicken und die Option Verweis hinzufügen wählen. Suchen Sie nach System.Messaging.Dll in der Liste der .NET-Komponenten, und wählen Sie diesen Eintrag aus. Dieser Namespace enthält die Klassen, die für die Kommunikation mit MSMQ-Warteschlangen verwendet werden.

  • Anschließend fügen Sie dem Projekt eine neue Klasse hinzu, indem Sie im Menü Datei auf die Option Neues Element hinzufügen klicken. Wählen Sie die Vorlage Klasse aus, und geben Sie ihr den Namen MQListen. Fügen Sie der Klasse auf oberster Ebene die folgenden using-Anweisungen hinzu:

    // C#
    using System.Threading;
    using System.Messaging;
    

    Über den System.Threading-Namespace können Sie auf alle erforderlichen Threadingfunktionen zugreifen, in diesem Fall auf die Thread-Klasse und den ThreadInterruptException-Konstruktor. Dieser Namespace bietet zwar noch weitere komplexe Features, deren Erörterung würde jedoch den Rahmen dieses Artikels sprengen. Der System.Messaging-Namespace bietet Zugriff auf die MSMQ-Funktionalität, einschließlich das Senden und Empfangen von Meldungen über eine Warteschlange. In diesem Beispiel verwenden Sie die MessageQueue-Klasse für den Empfang von Meldungen. Zudem müssen Sie using System.Threading dem Code des Hauptformulars hinzufügen.

Nachdem nun alle Verweise festgelegt sind, können Sie mit dem Schreiben des Codes beginnen.

Arbeitsthreads
Der erste Schritt besteht darin, die MQListen-Klasse zu erstellen, die die gesamte Threadarbeit kapselt. Fügen Sie den folgenden Code in MQListen ein.

// C#
public class MQListen
{   
   private string m_MachineName;
   private string m_QueueName;
   // Konstruktor akzeptiert die erforderlichen Warteschlangeninformationen.
   public MQListen(string MachineName, string QueueName)
   {
      m_MachineName = MachineName;
      m_QueueName = QueueName;
   }
   // Einzige Methode, die jeder Thread für das Warten auf MQ-Meldungen verwendet
   public void Listen()
   {
      // MessageQueue-Objekt erstellen.
      System.Messaging.MessageQueue MQ  = new 
System.Messaging.MessageQueue();
      // Pfadeigenschaft im MessageQueue-Objekt festlegen.
      MQ.Path = m_MachineName + "\\private$\\" + m_QueueName;
      // Message-Objekt erstellen.
System.Messaging.Message Message = new    
System.Messaging.Message();        
      // Wiederholen, bis Interrupt empfangen wird.
      while (true)
      {
         Try
         {
// Sleep aufrufen, um den ausgelösten Interrupt abzufangen.
System.Threading.Thread.Sleep(100);
// Message-Objekt auf das Ergebnis der Receive-Funktion setzen.
// Zeitspanne (Tage, Stunden, Minuten, Sekunden).
            Message = MQ.Receive(new TimeSpan(0, 0, 0, 1));
            // Bezeichnung der empfangenen Meldung anzeigen
System.Windows.Forms.MessageBox.Show(" Label: " + Message.Label);
         }
         catch (ThreadInterruptedException e)
         {
// ThreadInterrupt vom Hauptthread abfangen und beenden.
            Console.WriteLine("Exiting Thread");
            Message.Dispose();
            MQ.Dispose();
            break;
         }
         catch (Exception GenericException)
         {
            // Alle ausgelösten Ausnahmen in Receive abfangen.
            Console.WriteLine(GenericException.Message);
         }
      }
   }
}

Codeerörterung
Die MQListen-Klasse hat neben dem Konstruktor eine weitere Funktion. Diese Funktion kapselt die gesamte Arbeit, die von den einzelnen Arbeitsthreads geleistet wird. Im Hauptthread übergeben Sie einen Verweis auf diese Funktion an den Threadkonstruktor, so dass sie beim Starten der Threads ausgeführt werden kann.

Die erste Aktion, die von Listen ausgeführt wird, ist das Einrichten eines Warteschlangenobjekts. Der MessageQueue-Konstruktor ist mit drei Implementierungen überladen. Die erste Implementierung verarbeitet zwei Argumente: ein Zeichenfolgenargument, das den Speicherort der Warteschlange angibt, auf die gewartet werden soll, und einen Booleschen Wert, der angibt, ob die erste Anwendung, die auf die Warteschlange zugreift, exklusive Leserechte für die Warteschlange erhalten soll. Die zweite Implementierung verarbeitet nur das Warteschlangenpfad-Argument, und die dritte Implementierung verwendet keine Argumente. Zwecks Vereinfachung können Sie die dritte Implementierung verwenden und den Pfad in der nächsten Zeile zuweisen.

Sobald Sie über einen Verweis auf die Warteschlange verfügen, müssen Sie ein Meldungsobjekt erstellen. Der Meldungskonstruktor verfügt ebenfalls über drei Implementierungen. Die ersten beiden Implementierungen sind in den Fällen verfügbar, in denen Sie eine Meldung in eine Warteschlange schreiben möchten. Sie verwenden ein Objekt, das in den Meldungstext eingefügt wird, und ein IMessageFormatter-Objekt, das definiert, wie das Objekt in den Meldungstext serialisiert werden soll. In diesem Fall lesen Sie aus der Warteschlange, daher initialisieren Sie ein leeres Meldungsobjekt.

Nachdem die Objekte initialisiert wurden, beginnt die Hauptschleife, die die gesamte Arbeit erledigt. Wenn der Hauptthread zu einem späteren Zeitpunkt Interrupt aufruft, um diese Threads anzuhalten, werden sie nur unterbrochen, falls sie warten, deaktiviert oder in einer Verknüpfung enthalten sind. Falls sie keinen dieser drei Stati aufweisen, werden sie erst beim nächsten Erreichen eines solchen Status unterbrochen. Um sicherzustellen, dass die Arbeitsthreads in einen Warte-, deaktivierten oder Verknüpfungsstatus wechseln, rufen Sie die Sleep-Methode auf.

Diese befindet sich im System.Threading-Namespace. Die Sleep-Methode sollte C++- und Visual-Basic-Entwicklern vertraut sein, die bereits mit der Sleep-Funktion der Windows-API gearbeitet haben. Sie verwendet nur ein Argument: Die Anzahl von Millisekunden, während derer der Thread deaktiviert sein sollte. Wenn Sie Sleep nie aufrufen, befinden sich die Arbeitsthreads nie in einem Status, in dem sie die Interruptanforderungen empfangen können, und fahren somit endlos fort, bis Sie den Prozess manuell beenden.

Es gibt zwei Implementierungen der MQ-Receive-Methode. Die erste Implementierung verarbeitet nichts und wartet endlos auf den Empfang einer Meldung. Die zweite Implementierung (die in diesem Beispiel verwendet wird) definiert einen Timeout mit einem TimeSpan-Objekt. Der TimeSpan-Konstruktor hat vier Argumente: Tage, Stunden, Minuten und Sekunden. In diesem Beispiel wartet die Receive-Methode vor Timeout und Rückgabe eine Sekunde lang.

Wenn eine Meldung empfangen wird, wird sie dem zuvor erstellten Meldungsobjekt zugewiesen und ist anschließend zur Verarbeitung verfügbar. Dieser Beispielcode öffnet ein Meldungsfeld mit der Bezeichnung und verwirft die Meldung. Wenn Sie diesen Code für die Praxis anpassen möchten, fügen Sie hier Meldungsverarbeitungscode ein.

Wenn der Arbeitsthread die Interrupt-Anforderung empfängt, löst er eine ThreadInterruptedException-Ausnahme aus. Zum Auffangen dieser Ausnahme wrappen Sie die Funktionen Sleep und Receive in einem try-catch-Block. Sie sollten zwei Catches angeben: den ersten zum Abfangen der Interruptausnahme und den zweiten zum Verarbeiten von Ausnahmefehlern, die abgefangen werden. Wenn die Interruptausnahme abgefangen wird, schreiben Sie zuerst in das Debugfenster, das der Thread schließt. Dann rufen Sie die Dispose-Methode für das Warteschlangenobjekt und das Meldungsobjekt auf, um sicherzustellen, dass der gesamte Arbeitsspeicher bereinigt und an den Garbagecollector gesendet wird. Anschließend durchbrechen Sie die while-Schleife.

Sobald die Funktion die while-Schleife verlässt, wird der entsprechende Thread mit Code 0 beendet. Im Debugfenster sehen Sie eine Meldung wie 'Der Thread '<name>' (0x660) hat mit Code 0 (0x0) geendet'. Der Thread ist nun ohne Kontext und wird automatisch gelöscht. Weder der Hauptthread noch der Arbeitsthread muss eine spezielle Aktion zur Bereinigung durchführen.

Hauptformular
Der nächste Schritt besteht darin, dem Formular den Code hinzuzufügen, um die Arbeitsthreads zu erstellen und die MQListen-Klasse für jeden einzelnen Thread zu starten. Fügen Sie dem Formular zunächst die folgende Funktion hinzu:

// C#
private void StartThreads()
{
   int LoopCounter; // Threadanzahl
   StopListeningFlag = false; // Flaggen, um zu verfolgen, ob die Arbeitsthreads
                               // angehalten werden sollten.
   // Ein Array von 5 Threads als Arbeitsthreads deklarieren.
   Thread[] ThreadArray = new Thread[5];
// Die Klasse deklarieren, die den gesamten Code für die Arbeitsthreads enthält.
   MQListen objMQListen = new
MQListen(this.ServerName.Text,this.QueueName.Text); 
   for (LoopCounter = 0; LoopCounter < NUMBER_THREADS; LoopCounter++)
   {
      // Threadobjekt erstellen.
      ThreadArray[LoopCounter] = new Thread(new
ThreadStart(objMQListen.Listen));
      // Durch das Starten des Threads wird der ThreadStart-Delegat aufgerufen.
      ThreadArray[LoopCounter].Start();
   }
   statusBar1.Text = LoopCounter.ToString() + " listener threads started";
   while (!StopListeningFlag)
   {
      // Warten, bis der Benutzer auf die Stopp-Schaltfläche klickt.
      // Während des Wartens kann das System andere Ereignisse verarbeiten.
      System.Windows.Forms.Application.DoEvents();
   }
   statusBar1.Text = "Stop request received, stopping threads";
   // Interruptanforderung an jeden Thread senden.
   for (LoopCounter = 0;LoopCounter < NUMBER_THREADS; LoopCounter++)
   {      
      ThreadArray[LoopCounter].Interrupt();
   }
   statusBar1.Text = "All Threads have been stopped";
}

Codeerörterung
Diese Funktion erstellt zunächst ein Threadarray, das aus fünf Threads besteht. Dieses Array enthält Verweise auf alle Threadobjekte für eine künftige Verwendung.

Der Konstruktor für die MQListen-Klasse verarbeitet zwei Argumente: den Namen des Computers, der die Meldungswarteschlangen hostet, und den Namen der Warteschlange, auf die Sie warten möchten. Der Konstruktor weist diese Argumente unter Verwendung von Werten aus den Textfeldern zu.

Zum Erstellen der Threads starten Sie eine Schleife, um jedes Threadobjekt zu initialisieren. Der Threadkonstruktor erfordert die Übergabe eines Delegaten, der auf die Funktion zeigt, die bei Aufruf der Start-Methode aufgerufen wird. Sie möchte, dass der Thread mit der Verwendung der MQListen.Listen-Funktion beginnt, es handelt sich jedoch nicht um einen Delegaten. Um die Anforderungen des Threadkonstruktors zu erfüllen, müssen Sie ein ThreadStart-Objekt übergeben, das einen Delegaten mit einem Funktionsnamen erstellen wird. In diesem Fall übergeben Sie dem ThreadStart-Objekt einen Verweis auf die MQListen.Listen-Funktion. Nach der Initialisierung dieses Arrayelements können Sie umgehend Start aufrufen, um den Thread auszulösen.

Sobald alle Threads gestartet wurden, aktualisieren Sie die Statusleiste im Formular mit einer entsprechenden Meldung. Während die Threads ausgeführt werden und auf Meldungen aus der Warteschlange warten, wartet der Hauptthread darauf, dass der Benutzer die Anwendung auffordert, das Warten abzubrechen. Dazu wird eine Schleife ausgeführt, bis Sie auf die Schaltfläche StopListening klicken. Dies ändert den Wert von StopListeningFlag. Innerhalb dieser Warteschleife können Sie der Anwendung über die Forms.Application.DoEvents-Methode gestatten, ggf. weitere Verarbeitungen durchzuführen. Entwickler, die mit Visual Basic vertraut sind, dürften bemerken, dass dies mit der DoEvents-Methode identisch ist. C++-erfahrene Entwickler werden große Ähnlichkeit mit dem Schreiben eines Nachrichtensystems feststellen.

Wenn der Benutzer auf die Schaltfläche StopListening klickt, wird diese Schleife beendet und der Code zum Herunterfahren des Threads gestartet. Um jeden Thread herunterzufahren, muss der Code das Threadarray durchlaufen und an jeden Thread ein Interruptsignal senden. Rufen Sie in dieser Schleife die Interrupt-Methode für jedes Threadobjekt im Array auf. Bis zum Aufruf dieser Methode wird der Code in der MQListen-Klasse weiterhin normal ausgeführt. Daher können Sie Interrupt für jeden Arbeitsthread aufrufen, unabhängig davon, ob er gerade einen anderen Vorgang verarbeitet. Die Threadklasse verarbeitet die Bereinigung jedes Threads, sobald dieser abgeschlossen ist. Die abschließende Task besteht darin, die Statusleiste auf dem Hauptformular vor dem Beenden zu aktualisieren.
Jetzt müssen Sie den Code hinter den Schaltflächen hinzufügen. Fügen Sie dem Click-Ereignis der Schaltfläche StartListening den folgenden Code hinzu:

Dadurch wird die Statusleiste aktualisiert und die StartThreads-Methode aufgerufen. Für die Schaltfläche StopListening müssen Sie lediglich StopListeningFlag mit dem folgenden Code auf True setzen:

Der letzte Schritt besteht darin, die Formularebenenvariable für StopListeningFlag hinzuzufügen. Fügen Sie diese Zeile oben in den Formularcode ein:

Zum Testen Ihrer Anwendung können Sie MQWrite downloaden. Dies ist eine Beispielanwendung für das Schreiben in eine Meldungswarteschlange.

 

Potenzielle Probleme bei Multithreadingcode

Nachdem Sie den Beispielcode durchgearbeitet haben, verfügen Sie nun über die Tools, die für das Schreiben Ihrer eigenen Multithreading-Anwendung erforderlich sind. Threading kann sowohl die Leistung als auch die Skalierbarkeit bestimmter Anwendungen drastisch steigern. Sie sollten sich jedoch auch der Gefahren des Threading bewusst sein. Es gibt einige Situationen, in denen die Verwendung von Threads Ihrer Anwendung schaden kann. Threads können den Betrieb verlangsamen, unerwartete Ergebnisse auslösen oder sogar bewirken, dass Ihre Anwendung nicht mehr reagiert.

Wenn Sie mehrere Threads haben, sollten Sie sicherstellen, dass diese Threads nicht für das Erreichen eines bestimmten Punkts oder das Beenden aufeinander warten. Wenn dies nicht korrekt ausgeführt wird, kann ein Deadlock entstehen: Keiner der Threads wird abgeschlossen, da sie aufeinander warten.

Wenn mehrere Threads Zugriff auf eine Ressource erfordern, die nicht ohne weiteres gemeinsam genutzt werden kann (z.B. ein Diskettenlaufwerk, ein serieller Anschluss oder ein Infrarotanschluss), sollten Sie die Verwendung von Threads vermeiden oder eines der neueren Threadingtools, z.B. synclocks oder mutexes, für die Parallelitätsverwaltung verwenden. Wenn zwei Threads gleichzeitig auf eine dieser Ressourcen zuzugreifen versuchen, erhält einer der Threads keinen Ressourcenzugriff oder beschädigt die Daten.

Ein weiteres gängiges Problem beim Threading ist eine Racebedingung. Wenn ein Thread Daten in eine Datei schreibt, während ein zweiter Thread aus dieser Datei liest, wissen Sie nicht, welcher Thread zuerst fertig wird. Dies wird als Racebedingung bezeichnet, da die beiden Threads sich quasi ein Wettrennen zum Dateiende liefern. Wenn der lesende Thread vor dem schreibenden Thread fertig wird, werden unbekannte Ergebnisse zurückgegeben.

Bei der Verwendung von Threads sollten Sie zudem berücksichtigen, ob alle Threads völlig unabhängig voneinander arbeiten können. Wenn die Threads Daten bidirektional übergeben und empfangen müssen, sollten Sie darauf achten, dass diese Daten relativ einfach sind. Beim Übergeben von komplexen Objekten entstehen hohe Marshallingkosten für das Hin- und Herverschieben dieser Objekte. Dadurch entsteht zusätzlicher Aufwand für das Betriebssystem, und die Gesamtleistung sinkt.

Eine weitere Erwägung sind die Kosten, die bei der Übergabe Ihres Codes an einen anderen Entwickler entstehen können. .NET macht Threading zwar erheblich einfacher, der nächste Entwickler, der Ihren Code verwaltet, muss sich jedoch mit Threading auskennen, um mit dem Code arbeiten zu können. Dies ist zwar kein Grund, nicht mit Threads zu arbeiten, jedoch ein guter Anlass für das Bereitstellen von geeigneten Codekommentaren.

Diese Aspekte sollten Sie zwar nicht vom Threading als solches abhalten. Sie sollten diese Punkte jedoch beim Entwickeln Ihrer Anwendung im Hinterkopf haben und auf dieser Grundlage entscheiden, ob Sie Threading verwenden oder nicht. Eine Erörterung der Methoden zur Problemumgehung würde leider den Rahmen dieses Whitepapers sprengen. Wenn Sie sich für die Verwendung von Threads entschieden haben, aber auf einige der oben beschriebenen Probleme stoßen, können Sie diese mit synclocks oder mutexes zu lösen versuchen.

 

Schlussfolgerung

Anhand dieser Informationen können Sie Anwendungen schreiben, die Threading nutzen. Dabei sollten Sie jedoch stets die potenziellen Probleme berücksichtigen. Bei richtiger Verwendung kann Threading die Leistung und Skalierbarkeit von Anwendungen gegenüber Single-Threading-Anwendungen erheblich steigern. Bei falscher Verwendung hingegen hat Threading u.U. die gegenteilige Wirkung und kann zu instabilen Anwendungen führen.