정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

Monitor.Enter 메서드 (Object)

2013-12-13

지정된 개체의 단독 잠금을 가져옵니다.

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에 전달하면 개체로 boxing됩니다. 같은 변수를 Enter에 다시 전달하면 별도의 개체로 boxing되며 스레드를 차단하지 않습니다. 이 경우에는 Monitor가 보호하고 있을 것으로 추측되는 코드가 보호되지 않습니다. 뿐만 아니라 변수를 Exit에 전달해도 별도의 다른 개체가 계속 만들어집니다. Exit에 전달된 개체는 Enter에 전달된 개체와 다르므로 MonitorSynchronizationLockException을 발생시킵니다. 자세한 내용은 개념 항목인 [33FE4AEF-B44B-42FD-9E72-C908E39E75DB]를 참조하세요.

C# tryfinally 블록(Visual Basic에서는 TryFinally)을 사용하여 모니터 해제를 확인하거나, tryfinally 블록으로 Exit 메서드를 래핑하는 C# lock 문(Visual Basic에서는 SyncLock)을 사용합니다.

다음 예제에서는 Enter, TryEnter, TryEnterExit 메서드를 사용하는 방법을 보여 줍니다. 이 예제에서는 Monitor의 메서드를 사용하여 private Queue<T>을 보호하는 제네릭 SafeQueue 클래스를 정의합니다.

중요중요:

잠금을 가져올 때는 예외가 발생하더라도 잠금이 해제되도록 항상 try/finally를 사용합니다. 예를 들어 이 예제의 Dequeue 메서드에서는 큐가 비어 있으면 예외가 발생합니다. 잠금을 가져오는 메서드는 try 블록 외부에 있어야 해당 메서드의 예외로 인해 finally 블록이 실행되지 않습니다. C# lock 및 Visual Basic SyncLock 문은 EnterExit 메서드를 사용하여 구현합니다. lockSyncLock은 항상 try/finally 블록을 사용하여 Monitor를 보호하므로 EnterExit 메서드 대신 사용하는 것이 좋습니다.

이 예제에서는 SafeQueue<int>(Visual Basic에서는 SafeQueue(Of Integer))를 만들고 임의로 정수를 큐에 넣거나 큐에서 제거하는 세 개의 스레드를 시작합니다. 세 스레드가 모두 끝나면 예제에서 각 작업에 대한 통계를 출력합니다.

참고참고:

이 예제를 실행하려면 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

Windows Phone

표시: