Ich habe die Klassen- und CLR-Unterstützung für das Threading in DotGNU durchgeführt und habe ein paar Gedanken ...
Sofern Sie keine prozessübergreifenden Sperren benötigen, sollten Sie die Verwendung von Mutex & Semaphoren immer vermeiden. Diese Klassen in .NET sind Wrapper für Win32 Mutex und Semaphoren und ziemlich schwer (sie erfordern einen Kontextwechsel in den Kernel, der teuer ist - insbesondere, wenn Ihre Sperre nicht umstritten ist).
Wie bereits erwähnt, ist die C # -Sperranweisung Compiler-Magie für Monitor.Enter und Monitor.Exit (vorhanden innerhalb eines try / finally).
Monitore verfügen über einen einfachen, aber leistungsstarken Signal- / Wartemechanismus, den Mutexe über die Monitor.Pulse / Monitor.Wait-Methoden nicht haben. Das Win32-Äquivalent wären Ereignisobjekte über CreateEvent, die tatsächlich auch in .NET als WaitHandles vorhanden sind. Das Pulse / Wait-Modell ähnelt Unixs pthread_signal und pthread_wait, ist jedoch schneller, da es sich im unbestrittenen Fall vollständig um Operationen im Benutzermodus handeln kann.
Monitor.Pulse / Wait ist einfach zu bedienen. In einem Thread sperren wir ein Objekt, überprüfen ein Flag / einen Status / eine Eigenschaft und rufen Monitor.Wait auf, um die Sperre aufzuheben und zu warten, bis ein Impuls gesendet wird. Wenn die Wartezeit zurückkehrt, kehren wir zurück und überprüfen das Flag / den Status / die Eigenschaft erneut. Im anderen Thread sperren wir das Objekt, wenn wir das Flag / den Status / die Eigenschaft ändern, und rufen dann PulseAll auf, um alle abhörenden Threads zu aktivieren.
Oft möchten wir, dass unsere Klassen threadsicher sind, also setzen wir Sperren in unseren Code. Es ist jedoch häufig der Fall, dass unsere Klasse immer nur von einem Thread verwendet wird. Dies bedeutet, dass die Sperren unseren Code unnötig verlangsamen. Hier können clevere Optimierungen in der CLR dazu beitragen, die Leistung zu verbessern.
Ich bin mir nicht sicher, ob Microsoft Sperren implementiert, aber in DotGNU und Mono wird im Header jedes Objekts ein Sperrstatus-Flag gespeichert. Jedes Objekt in .NET (und Java) kann zu einer Sperre werden, sodass jedes Objekt dies in seinem Header unterstützen muss. In der DotGNU-Implementierung gibt es ein Flag, mit dem Sie eine globale Hashtabelle für jedes Objekt verwenden können, das als Sperre verwendet wird. Dies hat den Vorteil, dass für jedes Objekt ein 4-Byte-Overhead vermieden wird. Dies ist nicht besonders für den Speicher geeignet (insbesondere für eingebettete Systeme, die nicht stark mit Threads versehen sind), beeinträchtigt jedoch die Leistung.
Sowohl Mono als auch DotGNU verwenden effektiv Mutexe, um Sperren / Warten durchzuführen, verwenden jedoch Vergleichs- und Austauschoperationen im Spinlock-Stil , um die Notwendigkeit zu beseitigen, tatsächlich harte Sperren durchzuführen, sofern dies nicht wirklich erforderlich ist:
Hier sehen Sie ein Beispiel, wie Monitore implementiert werden können:
http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup