Threadsynchronisierung (C#)Thread Synchronization (C#)

In den folgenden Abschnitten werden Funktionen und Klassen beschrieben, die zum Synchronisieren des Zugriffs auf Ressourcen in Multithreadanwendungen verwendet werden können.The following sections describe features and classes that can be used to synchronize access to resources in multithreaded applications.

Einer der Vorteile bei der Verwendung von mehreren Threads in einer Anwendung ist, dass jeder Thread asynchron ausgeführt wird.One of the benefits of using multiple threads in an application is that each thread executes asynchronously. Für Windows-Anwendungen können zeitintensive Aufgaben im Hintergrund ausgeführt werden, während das Anwendungsfenster und Steuerelement reaktionsfähig bleiben.For Windows applications, this allows time-consuming tasks to be performed in the background while the application window and controls remain responsive. Für Serveranwendungen bietet Multithreading die Möglichkeit, jede eingehende Aufforderung mit einem anderen Thread zu bearbeiten.For server applications, multithreading provides the ability to handle each incoming request with a different thread. Andernfalls würde jede neue Anforderung erst dann bearbeitet werden, nachdem die vorherige Anforderung vollständig erfüllt wurde.Otherwise, each new request would not get serviced until the previous request had been fully satisfied.

Die asynchrone Eigenschaft des Threads bedeutet jedoch, dass Zugriff auf Ressourcen, wie z.B. Dateihandles, Netzwerkverbindungen und Arbeitsspeicher, koordiniert werden muss.However, the asynchronous nature of threads means that access to resources such as file handles, network connections, and memory must be coordinated. Andernfalls könnten zwei oder mehr Threads auf die selbe Ressource zugreifen, wobei kein Thread weiß, was der andere macht.Otherwise, two or more threads could access the same resource at the same time, each unaware of the other's actions. Die Folge ist eine unvorhersehbare Datenbeschädigung.The result is unpredictable data corruption.

Für einfache Operationen bei ganzzahligen numerischen Datentypen kann das Synchronisieren von Threads mit Membern der Klasse Interlocked erreicht werden.For simple operations on integral numeric data types, synchronizing threads can be accomplished with members of the Interlocked class. Für alle anderen Datentypen sowie nicht threadsichere Ressourcen kann Multithreading nur mithilfe der Konstrukte in diesem Thema ausgeführt werden.For all other data types and non thread-safe resources, multithreading can only be safely performed using the constructs in this topic.

Hintergrundinformationen zur Multithreadprogrammierung finden Sie unter:For background information on multithreaded programming, see:

Das lock-SchlüsselwortThe lock Keyword

Die lock-Anweisung in C# kann verwendet werden, um sicherzugehen, dass ein Codeblock ohne Unterbrechung durch andere Threads vollständig ausgeführt werden kann.The C# lock statement can be used to ensure that a block of code runs to completion without interruption by other threads. Dies geschieht durch das Abrufen einer Sperre für gegenseitigen Ausschluss für ein bestimmtes Objekt für die Dauer des Codeblocks.This is accomplished by obtaining a mutual-exclusion lock for a given object for the duration of the code block.

Einer lock-Anweisung wird ein Objekt als Argument zugewiesen, gefolgt von einem Codeblock, der nur von einem Thread ausgeführt werden soll.A lock statement is given an object as an argument, and is followed by a code block that is to be executed by only one thread at a time. Zum Beispiel:For example:

public class TestThreading  
{  
    private System.Object lockThis = new System.Object();  

    public void Process()  
    {  

        lock (lockThis)  
        {  
            // Access thread-sensitive resources.  
        }  
    }  

}  

Das Argument, das dem Schlüsselwort lock bereitgestellt wird, muss ein Objekt sein, dass auf einem Verweistyp basiert und zur Definition des Geltungsbereichs der Sperre verwendet wird.The argument provided to the lock keyword must be an object based on a reference type, and is used to define the scope of the lock. Im obigen Beispiel ist der Geltungsbereich der Sperre auf die Funktion begrenzt, da kein Verweis auf das Objekt lockThis außerhalb der Funktion existiert.In the example above, the lock scope is limited to this function because no references to the object lockThis exist outside the function. Wenn solch ein Verweis existieren würde, würde sich der Geltungsbereich der Sperre bis zu diesem Objekt erweitern.If such a reference did exist, lock scope would extend to that object. Genau genommen wird das bereitgestellte Objekt nur verwendet, um ausschließlich die Ressource zu identifizieren, die in mehreren Threads vorkommt, sodass sie eine beliebige Klasseninstanz sein kann.Strictly speaking, the object provided is used solely to uniquely identify the resource being shared among multiple threads, so it can be an arbitrary class instance. Jedoch stellt dieses Objekt in der Praxis normalerweise die Ressource dar, für die die Threadsynchronisierung benötigt wird.In practice, however, this object usually represents the resource for which thread synchronization is necessary. Wenn z.B. ein Containerobjekt von mehreren Threads verwendet werden soll, kann der Container anschließend zum Sperren übergeben werden, und der synchronisierte Codeblock, der der Sperre folgt, würde dem Container folgen.For example, if a container object is to be used by multiple threads, then the container can be passed to lock, and the synchronized code block following the lock would access the container. Solange andere Threads denselben Container sperren, bevor sie darauf zugreifen, ist der Zugriff auf das Objekt anschließend sicher synchronisiert.As long as other threads locks on the same contain before accessing it, then access to the object is safely synchronized.

Es ist allgemein am Besten, das Sperren eines public-Typs oder einer Objektinstanz außerhalb der Kontrolle Ihrer Anwendung zu vermeiden.Generally, it is best to avoid locking on a public type, or on object instances beyond the control of your application. lock(this) kann z.B. problematisch sein, wenn auf die Instanz öffentlich zugegriffen werden kann, da ein Code außerhalb Ihrer Kontrolle vielleicht auch das Objekt sperrt.For example, lock(this) can be problematic if the instance can be accessed publicly, because code beyond your control may lock on the object as well. Das könnte eine Deadlock-Situation erzeugen, bei der zwei oder mehr Threads auf die Freigabe des gleichen Objekts warten.This could create deadlock situations where two or more threads wait for the release of the same object. Das Sperren von öffentlichen Datentypen kann im Gegensatz zu einem Objekt aus demselben Grund Probleme verursachen.Locking on a public data type, as opposed to an object, can cause problems for the same reason. Sperren von Zeichenfolgenliteralen ist besonders riskant, da Literalzeichenfolgen von der Common Language Runtime (CLR) internalisiert sind.Locking on literal strings is especially risky because literal strings are interned by the common language runtime (CLR). Das bedeutet, dass es eine Instanz jedes Zeichenfolgenliterals für das gesamte Programm gibt. Dieselben Objekte stellen das Literal in jeder laufenden Anwendungsdomäne bei allen Threads dar.This means that there is one instance of any given string literal for the entire program, the exact same object represents the literal in all running application domains, on all threads. Daher sperrt eine Sperre, die an einer Zeichenfolge mit dem gleichen Inhalt an einer beliebigen Stelle angebracht wurde, jede Instanz des Strings in der Anwendung.As a result, a lock placed on a string with the same contents anywhere in the application process locks all instances of that string in the application. Deshalb ist es am Besten, ein privates oder geschütztes Member zu sperren, dass nicht internalisiert ist.As a result, it is best to lock a private or protected member that is not interned. Einige Klassen bieten Member, die speziell fürs Sperren vorgesehen sind.Some classes provide members specifically for locking. Der Array-Typ stellt z.B. SyncRoot bereit.The Array type, for example, provides SyncRoot. Viele Auflistungstypen stellen auch einen SyncRoot-Member bereit.Many collection types provide a SyncRoot member as well.

Weitere Informationen zur lock-Anweisung finden Sie unter den folgenden Themen:For more information about the lock statement, see the following topics:

MonitoreMonitors

Monitore verhindern, so wie das Schlüsselwort lock, dass Codeblöcke von mehreren Threads gleichzeitig ausgeführt werden.Like the lock keyword, monitors prevent blocks of code from simultaneous execution by multiple threads. Die Methode Enter ermöglicht es einem einzigen Thread, mit den folgenden Anweisungen fortzufahren, bis der auszuführende Thread Exit aufruft.The Enter method allows one and only one thread to proceed into the following statements; all other threads are blocked until the executing thread calls Exit. Dies ist vergleichbar mit dem Einsatz des Schlüsselworts lock.This is just like using the lock keyword. Zum Beispiel:For example:

lock (x)  
{  
    DoSomething();  
}  

Das entspricht:This is equivalent to:

System.Object obj = (System.Object)x;  
System.Threading.Monitor.Enter(obj);  
try  
{  
    DoSomething();  
}  
finally  
{  
    System.Threading.Monitor.Exit(obj);  
}  

Die Verwendung des Schlüsselworts lock statt des direkten Gebrauchs der Klasse Monitor wird allgemein bevorzugt, da lock präziser ist und lock sicherstellt, dass der zugrunde liegende Monitor freigegeben wird, auch wenn der geschützte Code eine Ausnahme auslöst.Using the lock keyword is generally preferred over using the Monitor class directly, both because lock is more concise, and because lock insures that the underlying monitor is released, even if the protected code throws an exception. Dies erfolgt mit dem Schlüsselwort finally, das seinen zugehörigen Codeblock ausführt, egal ob eine Ausnahme ausgelöst wird.This is accomplished with the finally keyword, which executes its associated code block regardless of whether an exception is thrown.

Synchronisierungsereignisse und Wait-HandlesSynchronization Events and Wait Handles

Das Verwenden einer Sperre oder eines Monitors ist nützlich, um die gleichzeitige Ausführung einer threadempfindlichen Codesperre zu verhindern. Jedoch erlauben diese Konstrukte nicht, dass ein Thread ein Ereignis an einen anderen weitergibt.Using a lock or monitor is useful for preventing the simultaneous execution of thread-sensitive blocks of code, but these constructs do not allow one thread to communicate an event to another. Dafür sind Synchronisierungsereignisse erforderlich, die Objekte sind, die aus einem oder zwei Zuständen – signalisiert und nicht signalisiert – bestehen, die zum Aktivieren und Anhalten von Threads verwendet werden können.This requires synchronization events, which are objects that have one of two states, signaled and un-signaled, that can be used to activate and suspend threads. Threads können angehalten werden, indem man sie auf ein nicht signalisiertes Synchronisierungsereignis warten lässt, und können aktiviert werden, indem der Ereigniszustand in „signalisiert“ geändert wird.Threads can be suspended by being made to wait on a synchronization event that is unsignaled, and can be activated by changing the event state to signaled. Wenn ein Thread versucht, auf ein Ereignis zu warten, das bereits signalisiert ist, wird der Thread ohne Verzögerung fortgesetzt.If a thread attempts to wait on an event that is already signaled, then the thread continues to execute without delay.

Es gibt zwei Arten von Synchronisierungsereignissen: AutoResetEvent und ManualResetEvent.There are two kinds of synchronization events: AutoResetEvent, and ManualResetEvent. Sie unterscheiden sich nur dadurch, dass AutoResetEvent jedes Mal, wenn es einen Thread aktiviert, automatisch von „signalisiert“ auf „nicht signalisiert“ wechselt.They differ only in that AutoResetEvent changes from signaled to unsignaled automatically any time it activates a thread. Dagegen erlaubt ManualResetEvent es, eine beliebige Anzahl von Threads über den signalisierten Zustand zu aktivieren, und es wird nur in den signalisierten Zustand zurückkehren, wenn die Methode Reset aufgerufen wird.Conversely, a ManualResetEvent allows any number of threads to be activated by its signaled state, and will only revert to an unsignaled state when its Reset method is called.

Threads können zum Warten auf Ereignisse durch Aufrufen einer der Wait-Methoden, wie WaitOne, WaitAny oder WaitAll, veranlasst werden.Threads can be made to wait on events by calling one of the wait methods, such as WaitOne, WaitAny, or WaitAll. WaitHandle.WaitOne bewirkt, dass der Thread wartet, bis ein einzelnes Ereignis signalisiert wird, WaitHandle.WaitAny blockiert einen Thread, bis ein oder mehrere angegebene Ereignisse signalisiert werden, und WaitHandle.WaitAll blockiert den Thread, bis alle angegebenen Ereignisse signalisiert werden.WaitHandle.WaitOne causes the thread to wait until a single event becomes signaled, WaitHandle.WaitAny blocks a thread until one or more indicated events become signaled, and WaitHandle.WaitAll blocks the thread until all of the indicated events become signaled. Ein Ereignis wird signalisiert, wenn seine Methode Set aufgerufen wird.An event becomes signaled when its Set method is called.

Im folgenden Beispiel wird ein Thread von der Funktion Main erstellt und gestartet.In the following example, a thread is created and started by the Main function. Der neue Thread wartet mithilfe der Methode WaitOne auf ein Ereignis.The new thread waits on an event using the WaitOne method. Der Thread wird angehalten, bis das Ereignis durch den primären Thread, der die Funktion Main ausführt, signalisiert wird.The thread is suspended until the event becomes signaled by the primary thread that is executing the Main function. Sobald das Ereignis signalisiert wird, kommt der Hilfsthread zurück.Once the event becomes signaled, the auxiliary thread returns. In diesem Fall, da das Ereignis nur für eine Thread-Aktivierung verwendet wird, können entweder die Klassen AutoResetEvent oder ManualResetEvent verwendet werden.In this case, because the event is only used for one thread activation, either the AutoResetEvent or ManualResetEvent classes could be used.

using System;  
using System.Threading;  

class ThreadingExample  
{  
    static AutoResetEvent autoEvent;  

    static void DoWork()  
    {  
        Console.WriteLine("   worker thread started, now waiting on event...");  
        autoEvent.WaitOne();  
        Console.WriteLine("   worker thread reactivated, now exiting...");  
    }  

    static void Main()  
    {  
        autoEvent = new AutoResetEvent(false);  

        Console.WriteLine("main thread starting worker thread...");  
        Thread t = new Thread(DoWork);  
        t.Start();  

        Console.WriteLine("main thread sleeping for 1 second...");  
        Thread.Sleep(1000);  

        Console.WriteLine("main thread signaling worker thread...");  
        autoEvent.Set();  
    }  
}  

Mutex-ObjektMutex Object

Ein Mutex ähnelt einem Monitor; es wird verhindert, dass die gleichzeitige Ausführung eines Codeblocks durch mehrere Threads zur selben Zeit erfolgt.A mutex is similar to a monitor; it prevents the simultaneous execution of a block of code by more than one thread at a time. Tatsächlich ist der Name „Mutex“ eine Kurzform der Bezeichnung „mutually exclusive“ (einander ausschließend).In fact, the name "mutex" is a shortened form of the term "mutually exclusive." Im Gegensatz zu Monitoren kann ein Mutex jedoch verwendet werden, um Threads prozessübergreifend zu synchronisieren.Unlike monitors, however, a mutex can be used to synchronize threads across processes. Ein Mutex wird durch die Klasse Mutex dargestellt.A mutex is represented by the Mutex class.

Wenn ein Mutex für prozessübergreifende Synchronisierungen verwendet wird, wird es als benanntes Mutex bezeichnet, da es in einer anderen Anwendung verwendet werden soll, und deshalb nicht durch eine globale oder statische Variable geteilt werden kann.When used for inter-process synchronization, a mutex is called a named mutex because it is to be used in another application, and therefore it cannot be shared by means of a global or static variable. Es muss benannt werden, sodass beide Anwendungen auf das gleiche Mutex-Objekt zugreifen können.It must be given a name so that both applications can access the same mutex object.

Obwohl ein Mutex für prozessübergreifende Threadsynchronisierung verwendet werden kann, wird die Verwendung von Monitor generell bevorzugt, da Monitore speziell für .NET Framework entwickelt wurden und deshalb Ressourcen besser verwenden.Although a mutex can be used for intra-process thread synchronization, using Monitor is generally preferred, because monitors were designed specifically for the .NET Framework and therefore make better use of resources. Im Gegensatz dazu ist die Klasse Mutex ein Wrapper für ein Win32-Konstrukt.In contrast, the Mutex class is a wrapper to a Win32 construct. Obwohl es leistungsstärker als ein Monitor ist, braucht ein Mutex Interop-Übergänge, die rechenintensiver sind, als diejenigen, die von der Klasse Monitor benötigt werden.While it is more powerful than a monitor, a mutex requires interop transitions that are more computationally expensive than those required by the Monitor class. Ein Beispiel für die Verwendung eines Mutex finden Sie unter Mutexe.For an example of using a mutex, see Mutexes.

Interlocked-KlasseInterlocked Class

Sie können die Methoden der Klasse Interlocked verwenden, um Probleme zu verhindern, die auftreten, wenn mehrere Threads gleichzeitig versuchen, den selben Wert zu aktualisieren oder zu vergleichen.You can use the methods of the Interlocked class to prevent problems that can occur when multiple threads attempt to simultaneously update or compare the same value. Mit den Methoden dieser Klasse können Sie auf sichere Weise Werte eines beliebigen Threads erhöhen, verringern, tauschen und vergleichen.The methods of this class let you safely increment, decrement, exchange, and compare values from any thread.

ReaderWriter-SperrenReaderWriter Locks

In einigen Fällen möchten Sie möglicherweise eine Ressource nur dann sperren, wenn Daten gerade geschrieben werden, und mehreren Clients erlauben, gleichzeitig Daten zu lesen, wenn sie nicht aktualisiert werden.In some cases, you may want to lock a resource only when data is being written and permit multiple clients to simultaneously read data when data is not being updated. Die Klasse ReaderWriterLock gewährt exklusiven Zugriff auf eine Ressource, während ein Thread die Ressource ändert, erlaubt jedoch einen nicht exklusiven Zugriff beim Lesen der Ressource.The ReaderWriterLock class enforces exclusive access to a resource while a thread is modifying the resource, but it allows non-exclusive access when reading the resource. Reader/Writer-Sperren sind eine nützliche Alternative zu exklusiven Sperren, die andere Threads warten lassen, selbst wenn diese Threads keine Daten aktualisieren müssen.ReaderWriter locks are a useful alternative to exclusive locks, which cause other threads to wait, even when those threads do not need to update data.

DeadlocksDeadlocks

Threadsynchronisierung ist in Multithreadanwendungen unabdingbar; es besteht jedoch immer die Gefahr, eine deadlock zu erzeugen, bei dem mehrere Threads aufeinander warten, und die Anwendung unterbrochen wird.Thread synchronization is invaluable in multithreaded applications, but there is always the danger of creating a deadlock, where multiple threads are waiting for each other and the application comes to a halt. Ein Deadlock kann mit einer Situation verglichen werden, bei der Autos an einer Kreuzung halten und jede Person darauf wartet, dass die andere losfährt.A deadlock is analogous to a situation in which cars are stopped at a four-way stop and each person is waiting for the other to go. Es ist wichtig, Deadlocks zu vermeiden; der Schlüssel liegt in der sorgfältigen Planung.Avoiding deadlocks is important; the key is careful planning. Sie können häufig Deadlock-Situationen vorhersehen, indem Sie Multithreadanwendungen abbilden, bevor Sie mit dem Programmieren beginnen.You can often predict deadlock situations by diagramming multithreaded applications before you start coding.

Siehe auchSee Also

Thread
WaitOne
WaitAny
WaitAll
Join
Start
Sleep
Monitor
Mutex
AutoResetEvent
ManualResetEvent
Interlocked
WaitHandle
EventWaitHandle
System.Threading
Set
Monitor
Multithreaded Applications (C#) (Multithreadanwendungen (C#))Multithreaded Applications (C#)
lock-Anweisunglock Statement
MutexeMutexes
Interlocked-VorgängeInterlocked Operations
AutoResetEventAutoResetEvent
Datensynchronisierung für MultithreadingSynchronizing Data for Multithreading