War diese Seite hilfreich?
Ihr Feedback ist uns wichtig. Teilen Sie uns Ihre Meinung mit.
Weiteres Feedback?
1500 verbleibende Zeichen
Exportieren (0) Drucken
Alle erweitern
Wichtig Dieses Dokument enthält ggf. nicht die bewährten Methoden für die aktuelle Entwicklung. Außerdem sind Links zu Downloads und anderen Ressourcen u. U. nicht mehr gültig. Die aktuell empfohlene Version finden Sie hier.

Monitore

Monitor-Objekte können den Zugriff auf einen Codebereich synchronisieren, indem sie ein bestimmtes Objekt mit den Methoden Monitor.Enter, Monitor.TryEnter und Monitor.Exit sperren bzw. die Sperre aufheben. Wenn ein Codebereich gesperrt ist, können die Methoden Monitor.Wait, Monitor.Pulse und Monitor.PulseAll verwendet werden. Wait gibt die Sperre frei, falls diese gehalten wird, und wartet auf Benachrichtigung. Wait wird nach der Benachrichtigung beendet, und die Sperre wird erneut verhängt. Sowohl Pulse als auch PulseAll signalisieren dem nächsten Thread in der Warteschlange fortzufahren.

Monitor sperrt Objekte (d. h., Verweistypen), nicht Werttypen. Sie können an Enter und Exit zwar einen Werttyp übergeben, dieser wird jedoch für jeden Aufruf separat geschachtelt. Da durch jeden Aufruf ein separates Objekt erstellt wird, wird Enter nie blockiert, und der Code, der eigentlich geschützt werden soll, wird nicht wirklich synchronisiert. Darüber hinaus unterscheiden sich das an Exit und das an Enter übergebene Objekt, sodass Monitor eine SynchronizationLockException mit der folgenden Meldung auslöst: "Die Objektsynchronisationsmethode wurde von einem nicht synchronisierten Codeblock aufgerufen". Das folgende Beispiel verdeutlicht diese Probleme.

private int x;
// The next line creates a generic object containing the value of
// x each time the code is executed, so that Enter never blocks.
Monitor.Enter(x);
try {
    // Code that needs to be protected by the monitor.
}
finally {
    // Always use Finally to ensure that you exit the Monitor.
    // The following line creates another object containing 
    // the value of x, and throws SynchronizationLockException
    // because the two objects do not match.
    Monitor.Exit(x);
}

Wie das folgende Beispiel zeigt, können Sie eine Werttypvariable zwar vor dem Aufruf von Enter und Exit schachteln und dasselbe geschachtelte Objekt an beide Methoden übergeben, diese Vorgehensweise bietet jedoch keinerlei Vorteile. Änderungen an der Variable wirken sich nicht auf die geschachtelte Kopie aus, und es ist nicht möglich, den Wert in der geschachtelten Kopie zu ändern.

private Object o = x;

Es ist wichtig, zwischen der Verwendung von Monitor-Objekten und WaitHandle-Objekten zu unterscheiden. Monitor-Objekte sind rein verwaltete, vollständig übertragbare Objekte, die in Bezug auf die Ressourcenanforderungen des Betriebssystems effizienter sein können. WaitHandle-Objekte repräsentieren Objekte des Betriebssystems, die in der Lage sind, ihre Ausführung zu unterbrechen und zu warten. Sie sind für die Synchronisierung zwischen verwaltetem und nicht verwaltetem Code von Nutzen und verfügen über einige höhere Betriebssystemfunktionen, z. B. die Fähigkeit, auf viele Objekte gleichzeitig zu warten.

Im folgenden Codebeispiel wird die Verwendung der Monitor-Klasse (implementiert mit dem Compilerschlüsselwort) in Kombination mit der Interlocked-Klasse gezeigt.

using System;
using System.Threading;

// Note: The class whose internal public member is the synchronizing 
// method is not public; none of the client code takes a lock on the 
// Resource object.The member of the nonpublic class takes the lock on 
// itself. Written this way, malicious code cannot take a lock on 
// a public object.
class SyncResource {
   public void Access(Int32 threadNum) {
      // Uses Monitor class to enforce synchronization.
      lock (this) {
       // Synchronized: Despite the next conditional, each thread 
       // waits on its predecessor.
       if (threadNum % 2 == 0)
         Thread.Sleep(2000);
         Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum);
         Thread.Sleep(200);
         Console.WriteLine("Stop Synched Resource access (Thread={0})", threadNum);
      }
   }
}

// Without the lock, the method is called in the order in which threads reach it.
class UnSyncResource {
   public void Access(Int32 threadNum) {
    // Does not use Monitor class to enforce synchronization.
    // The next call throws the thread order.
    if (threadNum % 2 == 0)
      Thread.Sleep(2000);
     Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
     Thread.Sleep(200);
     Console.WriteLine("Stop UnSynched Resource access (Thread={0})", threadNum);
   }
}

public class App {
   static Int32 numAsyncOps = 5;
   static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
   static SyncResource SyncRes = new SyncResource();
   static UnSyncResource UnSyncRes = new UnSyncResource();

   public static void Main() {

      for (Int32 threadNum = 0; threadNum < 5; threadNum++) {
         ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource), threadNum);
      }

      // Wait until this WaitHandle is signaled.
     asyncOpsAreDone.WaitOne();
      Console.WriteLine("\t\nAll synchronized operations have completed.\t\n");

     // Reset the thread count for unsynchronized calls.
     numAsyncOps = 5;

      for (Int32 threadNum = 0; threadNum < 5; threadNum++) {
         ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource), threadNum);
      }

      // Wait until this WaitHandle is signaled.
     asyncOpsAreDone.WaitOne();
      Console.WriteLine("\t\nAll unsynchronized thread operations have completed.");
   }


   // The callback method's signature MUST match that of a 
   // System.Threading.TimerCallback delegate (it takes an Object 
   // parameter and returns void).
   static void SyncUpdateResource(Object state) {
     // This calls the internal synchronized method, passing 
     // a thread number.
      SyncRes.Access((Int32) state);

     // Count down the number of methods that the threads have called.
     // This must be synchronized, however; you cannot know which thread 
     // will access the value **before** another thread's incremented 
     // value has been stored into the variable.
      if (Interlocked.Decrement(ref numAsyncOps) == 0)
         asyncOpsAreDone.Set(); 
         // Announce to Main that in fact all thread calls are done.
   }

   // The callback method's signature MUST match that of a 
   // System.Threading.TimerCallback delegate (it takes an Object 
   // parameter and returns void).
   static void UnSyncUpdateResource(Object state) {
     // This calls the unsynchronized method, passing a thread number.
      UnSyncRes.Access((Int32) state);

     // Count down the number of methods that the threads have called.
     // This must be synchronized, however; you cannot know which thread 
     // will access the value **before** another thread's incremented 
     // value has been stored into the variable.
      if (Interlocked.Decrement(ref numAsyncOps) == 0)
         asyncOpsAreDone.Set(); 
         // Announce to Main that in fact all thread calls are done.
   }
}

Siehe auch

Referenz

Monitor

Weitere Ressourcen

Threadingobjekte und -features

Anzeigen:
© 2015 Microsoft