情報
要求されたトピックは次のとおりです。しかし、このトピックはこのライブラリには含まれていません。

Monitor.Enter メソッド (Object)

2013/12/12

指定したオブジェクトの排他ロックを取得します。

Namespace:  System.Threading
アセンブリ:  mscorlib (mscorlib.dll 内)

public static void Enter(
	Object obj
)

パラメーター

obj
型: System.Object
モニター ロックを取得する対象となるオブジェクト。

例外条件
ArgumentNullException

obj パラメーターが null です。

Enter を使用して、パラメーターとして渡されたオブジェクトで Monitor を取得します。他のスレッドがそのオブジェクトで Enter を実行し、対応する Exit をまだ実行していない場合、現在のスレッドは他のスレッドがオブジェクトを解放するまでブロックします。同じスレッドがブロックを行わずに Enter を複数回呼び出すことは有効ですが、Exit を同じ回数呼び出さないと、そのオブジェクトで待機中の他のスレッドはブロックを解除できません。

値型ではなく、オブジェクト (つまり参照型) をロックするには Monitor を使用します。値型の変数を Enter に渡すと、これはオブジェクトとしてボックス化されます。同じ変数をもう一度 Enter に渡した場合、この変数は別のオブジェクトとしてボックス化され、スレッドはブロックしません。Monitor がプロテクトしていると推定されるコードは、プロテクトされません。また、この変数を Exit に渡すと、さらに別のオブジェクトが作成されます。Exit に渡されたオブジェクトは Enter に渡されたオブジェクトと異なるため、MonitorSynchronizationLockException をスローします。詳細については、概念に関するトピック「[33FE4AEF-B44B-42FD-9E72-C908E39E75DB]」を参照してください。

C# tryfinally ブロック (Visual Basic の場合は TryFinally) を使用して、モニターを確実に解放するか、C# lock ステートメント (Visual Basic の場合は SyncLock) を使用して、Exit メソッドを tryfinally ブロック内でラップします。

EnterTryEnterTryEnterExit の各メソッドを使用する方法を次の例に示します。この例では、SafeQueue ジェネリック クラスを定義します。このクラスは、Monitor のメソッドを使用して、プライベートの Queue<T> を保護します。

重要: 重要:

ロックを取得するときは、例外がスローされてもロックが解放されるように、必ず try/finally を使用します。たとえば、この例の Dequeue メソッドは、キューが空の場合に例外をスローします。メソッド内の例外によって finally ブロックが実行されないように、ロックを取得するメソッドは try ブロックの外側に置く必要があります。C# の lock ステートメントと Visual Basic の SyncLock ステートメントは、Enter メソッドと Exit メソッドを使用して実装されています。Enter メソッドと Exit メソッドの代わりに、これらのステートメントを使用することをお勧めします。lockSyncLock を使用すると、常に try/finally ブロックが使用されて Monitor が保護されます。

この例では、SafeQueue<int> (Visual Basic の場合は SafeQueue(Of Integer)) を作成し、整数をランダムにキューに出し入れする 3 つのスレッドを開始します。3 つのスレッドがすべて終了したら、各操作の統計情報を出力します。

メモメモ:

この例を実行するには、「Windows Phone での静的 TextBlock コントロールのあるコード例のビルド」を参照してください。


using System;
using System.Threading;
using System.Collections.Generic;
using System.Text;

class SafeQueue<T>
{
   // A queue that is protected by Monitor.
   private Queue<T> m_inputQueue = new Queue<T>();

   // Lock the queue and add an element.
   public void Enqueue(T qValue)
   {
      // Request the lock, and block until it is obtained.
      Monitor.Enter(m_inputQueue);
      try
      {
         // When the lock is obtained, add an element.
         m_inputQueue.Enqueue(qValue);
      }
      finally
      {
         // Ensure that the lock is released.
         Monitor.Exit(m_inputQueue);
      }
   }

   // Try to add an element to the queue: Add the element to the queue 
   // only if the lock is immediately available.
   public bool TryEnqueue(T qValue)
   {
      // Request the lock.
      if (Monitor.TryEnter(m_inputQueue))
      {
         try
         {
            m_inputQueue.Enqueue(qValue);
         }
         finally
         {
            // Ensure that the lock is released.
            Monitor.Exit(m_inputQueue);
         }
         return true;
      }
      else
      {
         return false;
      }
   }

   // Try to add an element to the queue: Add the element to the queue 
   // only if the lock becomes available during the specified time
   // interval.
   public bool TryEnqueue(T qValue, int waitTime)
   {
      // Request the lock.
      if (Monitor.TryEnter(m_inputQueue, waitTime))
      {
         try
         {
            m_inputQueue.Enqueue(qValue);
         }
         finally
         {
            // Ensure that the lock is released.
            Monitor.Exit(m_inputQueue);
         }
         return true;
      }
      else
      {
         return false;
      }
   }

   // Lock the queue and dequeue an element.
   public T Dequeue()
   {
      T retval;

      // Request the lock, and block until it is obtained.
      Monitor.Enter(m_inputQueue);
      try
      {
         // When the lock is obtained, dequeue an element.
         retval = m_inputQueue.Dequeue();
      }
      finally
      {
         // Ensure that the lock is released.
         Monitor.Exit(m_inputQueue);
      }

      return retval;
   }

   // Delete all elements that equal the given object.
   public int Remove(T qValue)
   {
      int removedCt = 0;

      // Wait until the lock is available and lock the queue.
      Monitor.Enter(m_inputQueue);
      try
      {
         int counter = m_inputQueue.Count;
         while (counter>0)
            //Check each element.
         {
            T elem = m_inputQueue.Dequeue();
            if (!elem.Equals(qValue))
            {
               m_inputQueue.Enqueue(elem);
            }
            else
            {
               // Keep a count of items removed.
               removedCt += 1;
            }
            counter = counter-1;
         }
      }
      finally
      {
         // Ensure that the lock is released.
         Monitor.Exit(m_inputQueue);
      }

      return removedCt;
   }

   // Print all queue elements.
   public string PrintAllElements()
   {
      StringBuilder output = new StringBuilder();

      //Lock the queue.
      Monitor.Enter(m_inputQueue);
      try
      {
         foreach( T elem in m_inputQueue )
         {
            // Print the next element.
            output.AppendLine(elem.ToString());
         }
      }
      finally
      {
         // Ensure that the lock is released.
         Monitor.Exit(m_inputQueue);
      }

      return output.ToString();
   }
}

public class Example
{
   private static System.Windows.Controls.TextBlock outputBlock;
   private static SafeQueue<int> q = new SafeQueue<int>();
   private static int threadsRunning = 0;
   private static int[][] results = new int[3][];

   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      outputBlock.FontFamily = new System.Windows.Media.FontFamily("Courier New");
      outputBlock.Text = "Working...\n";
      Example.outputBlock = outputBlock;

      for(int i = 0; i < 3; i++)
      {
         Thread t = new Thread(ThreadProc);
         t.Start(i);
         Interlocked.Increment(ref threadsRunning);
      }
   }

   private static void ThreadProc(object state)
   {
      DateTime finish = DateTime.Now.AddSeconds(10);
      Random rand = new Random();
      int[] result = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
      int threadNum = (int) state;

      while (DateTime.Now < finish)

      {
         int what = rand.Next(250);
         int how = rand.Next(100);

         if (how < 16)
         {
            q.Enqueue(what);
            result[(int)ThreadResultIndex.EnqueueCt] += 1;
         }
         else if (how < 32)
         {
            if (q.TryEnqueue(what))
            {
               result[(int)ThreadResultIndex.TryEnqueueSucceedCt] += 1;
            }
            else
            {
               result[(int)ThreadResultIndex.TryEnqueueFailCt] += 1;
            }
         }
         else if (how < 48)
         {
            // Even a very small wait significantly increases the success 
            // rate of the conditional enqueue operation.
            if (q.TryEnqueue(what, 10))
            {
               result[(int)ThreadResultIndex.TryEnqueueWaitSucceedCt] += 1;
            }
            else
            {
               result[(int)ThreadResultIndex.TryEnqueueWaitFailCt] += 1;
            }
         }
         else if (how < 96)
         {
            result[(int)ThreadResultIndex.DequeueCt] += 1;
            try
            {
               q.Dequeue();
            }
            catch
            {
               result[(int)ThreadResultIndex.DequeueExCt] += 1;
            }
         }
         else
         {
            result[(int)ThreadResultIndex.RemoveCt] += 1;
            result[(int)ThreadResultIndex.RemovedCt] += q.Remove(what);
         }         
      }

      results[threadNum] = result;

      if (0 == Interlocked.Decrement(ref threadsRunning))      
      {
         StringBuilder sb = new StringBuilder(
            "                               Thread 1 Thread 2 Thread 3    Total\n");

         for(int row = 0; row < 9; row++)
         {
            int total = 0;
            sb.Append(titles[row]);

            for(int col = 0; col < 3; col++)
            {
               sb.Append(String.Format("{0,9}", results[col][row]));
               total += results[col][row];
            }

            sb.AppendLine(String.Format("{0,9}", total));
         }

         outputBlock.Dispatcher.BeginInvoke(displayHelper, sb.ToString());
      }
   }

   private static string[] titles = {
      "Enqueue                       ", 
      "TryEnqueue succeeded          ", 
      "TryEnqueue failed             ", 
      "TryEnqueue(T, wait) succeeded ", 
      "TryEnqueue(T, wait) failed    ", 
      "Dequeue attempts              ", 
      "Dequeue exceptions            ", 
      "Remove operations             ", 
      "Queue elements removed        "};

   private enum ThreadResultIndex
   {
      EnqueueCt, 
      TryEnqueueSucceedCt, 
      TryEnqueueFailCt, 
      TryEnqueueWaitSucceedCt, 
      TryEnqueueWaitFailCt, 
      DequeueCt, 
      DequeueExCt, 
      RemoveCt, 
      RemovedCt
   };

   // In order to update the TextBlock object, which is on the UI thread, you must
   // make a cross-thread call by using the Dispatcher object that is associated 
   // with the TextBlock. The DisplayOutput helper method and its delegate, 
   // displayHelper, are used by the BeginInvoke method of the Dispatcher object
   // to append text to the TextBlock. 
   //
   private static Action<string> displayHelper = new Action<string>(DisplayOutput);
   private static void DisplayOutput(string msg)
   {
      outputBlock.Text += msg;
   }
}

/* This example produces output similar to the following:

Working...
                              Thread 1 Thread 2 Thread 3    Total
Enqueue                          65947   108269    71071   245287
TryEnqueue succeeded             66084   108631    71218   245933
TryEnqueue failed                  105      168      130      403
TryEnqueue(T, wait) succeeded    66658   108973    71695   247326
TryEnqueue(T, wait) failed           2        2        1        5
Dequeue attempts                199390   326435   214917   740742
Dequeue exceptions                3508     5380     3929    12817
Remove operations                16591    27104    17924    61619
Queue elements removed            2921     4690     2982    10593
 */


Windows Phone OS

サポート: 8.0, 7.1, 7.0

表示: