C# Multithreading
ThreadEin Thread ist definiert als der Ausführungspfad eines Programms. Jeder Thread definiert einen eindeutigen Kontrollfluss. Wenn Ihre Anwendung komplexe und zeitaufwändige Vorgänge umfasst, ist es oft von Vorteil, verschiedene Thread-Ausführungspfade einzurichten, wobei jeder Thread eine bestimmte Arbeit ausführt.
Thread istLeichtgewichtiger ProzessEin häufiges Beispiel für die Verwendung von Threads ist die Implementierung von paralleler Programmierung in modernen Betriebssystemen. Die Verwendung von Threads vermeidet die Verschwendung von CPU-Zyklen und verbessert die Effizienz von Anwendungen.
Die Programme, die wir bisher geschrieben haben, laufen als einzelner Prozess mit einem einzelnen Thread als laufende Instanz der Anwendung. Auf diese Weise kann die Anwendung jedoch nur eine Aufgabe gleichzeitig ausführen. Um mehrere Aufgaben gleichzeitig auszuführen, kann sie in kleinere Threads aufgeteilt werden.
Thread-Lebenszyklus
Der Thread-Lebenszyklus beginnt, wenn ein Objekt der Klasse System.Threading.Thread erstellt wird, und endet, wenn der Thread beendet wird oder seine Ausführung abgeschlossen hat.
Die verschiedenen Zustände im Thread-Lebenszyklus sind unten aufgeführt:
- Nicht gestarteter Zustand: Der Zustand, in dem eine Thread-Instanz erstellt wurde, aber die Start-Methode nicht aufgerufen wurde.
- Bereit-Zustand: Der Zustand, in dem ein Thread bereit zur Ausführung ist und auf einen CPU-Zyklus wartet.
- Nicht ausführbarer Zustand: Ein Thread ist in den folgenden Fällen nicht ausführbar:
- Die Sleep-Methode wurde aufgerufen
- Die Wait-Methode wurde aufgerufen
- Blockiert durch einen E/A-Vorgang
- Toter Zustand: Der Zustand, in dem der Thread die Ausführung abgeschlossen hat oder abgebrochen wurde.
Hauptthread
In C# wirdSystem.Threading.Thread– die Thread-Klasse – für Thread-Arbeit verwendet. Sie ermöglicht das Erstellen und den Zugriff auf einzelne Threads in einer Multithread-Anwendung. Der erste in einem Prozess ausgeführte Thread wird als Hauptthread bezeichnet.Hauptthread。
Wenn ein C#-Programm mit der Ausführung beginnt, wird der Hauptthread automatisch erstellt. Mit derThread-Klasse erstellte Threads werden als Kindthreads des Hauptthreads bezeichnet. Sie können dieCurrentThread-Eigenschaft der Thread-Klasse verwenden, um auf den Thread zuzugreifen.
Das folgende Programm demonstriert die Ausführung des Hauptthreads:
Beispiel
using System.Threading;
namespace MultithreadingApplication
{
class MainThreadProgram
{
static void Main(string[] args)
{
Thread th = Thread.CurrentThread;
th.Name = "MainThread";
Console.WriteLine("This is {0}", th.Name);
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, liefert er die folgenden Ergebnisse:
This is MainThread
Allgemeine Eigenschaften und Methoden der Thread-Klasse
Die folgende Tabelle listet einige allgemeine Methoden der KlasseThreadund dieEigenschaft:
| Eigenschaft | Beschreibung |
|---|---|
| CurrentContext | Ruft den aktuellen Kontext ab, in dem der Thread ausgeführt wird. |
| CurrentCulture | Ruft die Kultur des aktuellen Threads ab oder legt sie fest. |
| CurrentPrincipal | Ruft den aktuellen Prinzipal des Threads ab oder legt ihn fest (für rollenbasierte Sicherheit). |
| CurrentThread | Ruft den aktuell ausgeführten Thread ab. |
| CurrentUICulture | Ruft die aktuelle Kultur ab oder legt sie fest, die vom Resource Manager verwendet wird, um zur Laufzeit kulturspezifische Ressourcen nachzuschlagen. |
| ExecutionContext | Ruft ein ExecutionContext-Objekt ab, das Kontextinformationen über den aktuellen Thread enthält. |
| IsAlive | Ruft einen Wert ab, der den Ausführungsstatus des aktuellen Threads angibt. |
| IsBackground | Ruft einen Wert ab oder legt einen Wert fest, der angibt, ob ein Thread ein Hintergrundthread ist. |
| IsThreadPoolThread | Ruft einen Wert ab, der angibt, ob ein Thread zum verwalteten Threadpool gehört. |
| ManagedThreadId | Ruft den eindeutigen Bezeichner des aktuellen verwalteten Threads ab. |
| Name | Ruft den Namen des Threads ab oder legt ihn fest. |
| Priorität | Ruft einen Wert ab oder legt einen Wert fest, der die Planungspriorität des Threads angibt. |
| ThreadState | Ruft einen Wert ab, der den Zustand des aktuellen Threads enthält. |
Die folgende Tabelle enthältThreadEinige allgemeine Methoden der KlasseMethoden:
| Laufende Nummer | Methodenname & Beschreibung |
|---|---|
| 1 | public void Abort() Löst eine ThreadAbortException für den Thread aus, für den diese Methode aufgerufen wird, um den Vorgang des Beendens dieses Threads einzuleiten. Der Aufruf dieser Methode beendet normalerweise den Thread. |
| 2 | public static LocalDataStoreSlot AllocateDataSlot() Reserviert einen unbenannten Datenslot für alle Threads. Verwenden Sie für bessere Leistung stattdessen Felder, die mit dem Attribut ThreadStaticAttribute gekennzeichnet sind. |
| 3 | public static LocalDataStoreSlot AllocateNamedDataSlot(
string name)
Reserviert einen benannten Datenslot für alle Threads. Verwenden Sie für bessere Leistung stattdessen Felder, die mit dem Attribut ThreadStaticAttribute gekennzeichnet sind. |
| 4 | public static void BeginCriticalRegion() Benachrichtigt den Host, dass die Ausführung gerade einen Codebereich betritt, in dem die Auswirkungen von Threadabbrüchen oder nicht behandelten Ausnahmen andere Aufgaben in der Anwendungsdomäne gefährden können. |
| 5 | public static void BeginThreadAffinity() Benachrichtigt den Host, dass verwalteter Code dabei ist, Anweisungen auszuführen, die von der Identität des aktuellen physischen Betriebssystemthreads abhängen. |
| 6 | public static void EndCriticalRegion() Benachrichtigt den Host, dass die Ausführung gerade einen Codebereich betritt, in dem Threadabbrüche oder nicht behandelte Ausnahmen nur die aktuelle Aufgabe betreffen. |
| 7 | public static void EndThreadAffinity() Benachrichtigt den Host, dass verwalteter Code die Ausführung von Anweisungen abgeschlossen hat, die von der Identität des aktuellen physischen Betriebssystemthreads abhängen. |
| 8 | public static void FreeNamedDataSlot(string name) Beseitigt die Zuordnung zwischen Namen und Slots für alle Threads im Prozess. Verwenden Sie für bessere Leistung stattdessen Felder, die mit dem ThreadStaticAttribute-Attribut gekennzeichnet sind. |
| 9 | public static Object GetData(
LocalDataStoreSlot slot
)
Ruft den Wert aus dem angegebenen Slot im aktuellen Thread innerhalb der aktuellen Domäne des aktuellen Threads ab. Verwenden Sie für bessere Leistung stattdessen Felder, die mit dem ThreadStaticAttribute-Attribut gekennzeichnet sind. |
| 10 | public static AppDomain GetDomain() Gibt die aktuelle Domäne zurück, in der der aktuelle Thread ausgeführt wird. |
| 11 | public static AppDomain GetDomainID() Gibt einen eindeutigen Anwendungsdomänenbezeichner zurück. |
| 12 | public static LocalDataStoreSlot GetNamedDataSlot(
string name
)
Sucht einen benannten Datenslot nach. Verwenden Sie für bessere Leistung stattdessen Felder, die mit dem ThreadStaticAttribute-Attribut gekennzeichnet sind. |
| 13 | public void Interrupt() Unterbricht einen Thread, der sich im Threadzustand WaitSleepJoin befindet. |
| 14 | public void Join() Blockiert den aufrufenden Thread, bis ein Thread beendet wird, während weiterhin das standardmäßige COM- und SendMessage-Nachrichtenpumping ausgeführt wird. Diese Methode weist verschiedene Überladungsformen auf. |
| 15 | public static void MemoryBarrier() Synchronisiert den Speicherzugriff wie folgt: Der Prozessor, der den aktuellen Thread ausführt, kann Anweisungen nicht so umordnen, dass Speicherzugriffe nach dem MemoryBarrier-Aufruf vor Speicherzugriffen ausgeführt werden, die vor dem MemoryBarrier-Aufruf erfolgen. |
| 16 | public static void ResetAbort() Hebt den für den aktuellen Thread angeforderten Abort auf. |
| 17 | public static void SetData(
LocalDataStoreSlot slot,
Object data
)
Legt Daten im angegebenen Slot im aktuell ausgeführten Thread für die aktuelle Domäne des Threads fest. Verwenden Sie für eine bessere Leistung stattdessen Felder, die mit dem ThreadStaticAttribute-Attribut markiert sind. |
| 18 | public void Start() Startet einen Thread. |
| 19 | public static void Sleep(
int millisecondsTimeout
)
Pausiert den Thread für einen bestimmten Zeitraum. |
| 20 | public static void SpinWait(
int iterations
)
Bewirkt, dass ein Thread für die durch den Parameter iterations definierte Zeitspanne wartet. |
| 21 | public static byte VolatileRead(
ref byte address
) public static double VolatileRead( ref double address ) public static int VolatileRead( ref int address ) public static Object VolatileRead( ref Object address ) Liest den Feldwert. Unabhängig von der Anzahl der Prozessoren oder dem Zustand des Prozessor-Caches ist der Wert der zuletzt von einem beliebigen Prozessor im Computer geschriebene Wert. Diese Methode weist verschiedene Überladungsformen auf. Hier werden nur einige Formen angegeben. |
| 22 | public static void VolatileWrite(
ref byte address,
byte value
) public static void VolatileWrite( ref double address, double value ) public static void VolatileWrite( ref int address, int value ) public static void VolatileWrite( ref Object address, Object value ) Schreibt sofort einen Wert in ein Feld, sodass der Wert für alle Prozessoren im Computer sichtbar ist. Diese Methode hat verschiedene Überladungsformen. Hier sind nur einige Formen angegeben. |
| 23 | public static bool Yield() Bewirkt, dass der aufrufende Thread einen anderen Thread ausführt, der bereit ist, auf dem aktuellen Prozessor ausgeführt zu werden. Der auszuführende Thread wird vom Betriebssystem ausgewählt. |
Threads erstellen
Threads werden durch Erweitern der Thread-Klasse erstellt. Die erweiterte Thread-Klasse ruft dieStart()-Methode auf, um die Ausführung des untergeordneten Threads zu starten.
Das folgende Programm veranschaulicht dieses Konzept:
Beispiel
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Untergeordneter Thread startet");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Untergeordneten Thread erstellen");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, liefert er die folgenden Ergebnisse:
In Main: Creating the Child thread Child thread starts
Threads verwalten
Die Thread-Klasse bietet verschiedene Methoden zum Verwalten von Threads.
Das folgende Beispiel demonstriertsleep()Die Verwendung der Methode, um einen Thread für eine bestimmte Zeitspanne anzuhalten.
Beispiel
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Untergeordneter Thread startet");
// Den Thread für 5000 Millisekunden anhalten
int sleepfor = 5000;
Console.WriteLine("Untergeordneter Thread für {0} Sekunden pausiert",
sleepfor / 1000);
Thread.Sleep(sleepfor);
Console.WriteLine("Untergeordneter Thread wird fortgesetzt");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Untergeordneten Thread erstellen");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, liefert er die folgenden Ergebnisse:
In Main: Creating the Child thread Child thread starts Child Thread Paused for 5 seconds Child thread resumes
Threads zerstören
Abort()-Methode wird verwendet, um den Thread zu zerstören.
Durch das Auslösen vonthreadabortexceptionwird der Thread zur Laufzeit abgebrochen. Diese Ausnahme kann nicht abgefangen werden. Wenn einfinally-Block vorhanden ist, wird die Steuerung anfinally übergeben.Block.
Das folgende Programm veranschaulicht dies:
Beispiel
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
try
{
Console.WriteLine("Untergeordneter Thread startet");
// Bis 10 zählen
for (int counter = 0; counter <= 10; counter++)
{
Thread.Sleep(500);
Console.WriteLine(counter);
}
Console.WriteLine("Untergeordneter Thread abgeschlossen");
}
catch (ThreadAbortException e)
{
Console.WriteLine("Thread-Abbruch-Ausnahme");
}
finally
{
Console.WriteLine("Thread-Ausnahme konnte nicht abgefangen werden");
}
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Untergeordneten Thread erstellen");
Thread childThread = new Thread(childref);
childThread.Start();
// Den Hauptthread für eine gewisse Zeit anhalten
Thread.Sleep(2000);
// Jetzt den untergeordneten Thread abbrechen
Console.WriteLine("In Main: Untergeordneter Thread wird abgebrochen");
childThread.Abort();
Console.ReadKey();
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, liefert er die folgenden Ergebnisse:
In Main: Creating the Child thread Child thread starts 0 1 2 In Main: Aborting the Child thread Thread Abort Exception Couldn't catch the Thread ExceptionWeitere Erweiterungen