Monitor.Enter Метод

Определение

Получает монопольную блокировку указанного объекта.

Перегрузки

Enter(Object)

Получает эксклюзивную блокировку указанного объекта.

Enter(Object, Boolean)

Получает монопольную блокировку указанного объекта и единым блоком задает значение, указывающее, была ли выполнена блокировка.

Enter(Object)

Получает эксклюзивную блокировку указанного объекта.

public static void Enter (object obj);

Параметры

obj
Object

Объект, для которого получается блокировка монитора.

Исключения

Параметр obj имеет значение null.

Примеры

В следующем примере показано использование метода Enter.

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 SafeQueue<int> q = new SafeQueue<int>();
   private static int threadsRunning = 0;
   private static int[][] results = new int[3][];

   static void Main()
   {
      Console.WriteLine("Working...");

      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));
         }

         Console.WriteLine(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
   };
}

/* This example produces output similar to the following:

Working...
                               Thread 1 Thread 2 Thread 3    Total
Enqueue                          277382   515209   308464  1101055
TryEnqueue succeeded             276873   514621   308099  1099593
TryEnqueue failed                   109      181      134      424
TryEnqueue(T, wait) succeeded    276913   514434   307607  1098954
TryEnqueue(T, wait) failed            2        0        0        2
Dequeue attempts                 830980  1544081   924164  3299225
Dequeue exceptions                12102    21589    13539    47230
Remove operations                 69550   129479    77351   276380
Queue elements removed            11957    22572    13043    47572
 */

Комментарии

Используется Enter для получения Monitor объекта, переданного в качестве параметра. Если другой поток выполнил объект, Enter но еще не выполнил соответствующий Exitобъект, текущий поток будет блокироваться, пока другой поток не выпустит объект. Этот поток может вызываться Enter несколько раз без блокировки, однако необходимо вызвать равное количество Exit вызовов, прежде чем другие потоки, ожидающие выполнения объекта, разблокируются.

Используется Monitor для блокировки объектов (то есть ссылочных типов), а не типов значений. При передаче переменной Enterтипа значения в объект она упаковается в виде объекта. При повторном передаче той же переменной Enter она упаковается в виде отдельного объекта, и поток не блокируется. В этом случае код, который Monitor якобы защищается, не защищен. Кроме того, при передаче переменной Exitв другой отдельный объект создается. Поскольку переданный Exit объект отличается от переданного Enterв объект , Monitor вызывает SynchronizationLockExceptionисключение. Дополнительные сведения см. в разделе " Мониторы концептуального раздела".

Interrupt может прерывать потоки, ожидающие ввода Monitor объекта. Будет ThreadInterruptedException создано исключение.

Использование C# try...finally block (Try...Finally в Visual Basic) для обеспечения выпуска монитора или использования оператора C# lock (SyncLockоператор в Visual Basic), который заключает Enter в оболочку и Exit методы в ...try``finally Блок.

См. также раздел

Применяется к

.NET 7 и другие версии
Продукт Версии
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7
.NET Framework 1.1, 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0

Enter(Object, Boolean)

Получает монопольную блокировку указанного объекта и единым блоком задает значение, указывающее, была ли выполнена блокировка.

public static void Enter (object obj, ref bool lockTaken);

Параметры

obj
Object

Объект, в котором следует ожидать.

lockTaken
Boolean

Результат попытки получить блокировку, переданную по ссылке. Входное значение должно равняться false. Выходное значение true, если блокировка получена; в противном случае — выходное значение false. Выходное значение задается, даже если при попытке получить блокировку возникает исключение.

Примечание. Если исключение не возникает, выходное значение этого метода всегда равно true.

Исключения

Входные данные для lockTaken — true.

Параметр obj имеет значение null.

Примеры

В следующем коде показан базовый шаблон использования перегрузки Enter(Object, Boolean) метода. Эта перегрузка всегда задает значение переменной, передаваемой параметру ref (ByRef в Visual Basic), lockTakenдаже если метод создает исключение, поэтому значение переменной является надежным способом проверки необходимости освобождения блокировки.

bool acquiredLock = false;

try
{
    Monitor.Enter(lockObject, ref acquiredLock);

    // Code that accesses resources that are protected by the lock.
}
finally
{
    if (acquiredLock)
    {
        Monitor.Exit(lockObject);
    }
}

Комментарии

Используется Enter для получения Monitor объекта, переданного obj в качестве параметра. Если другой поток выполнил объект, Enter но еще не выполнил соответствующий Exitобъект, текущий поток будет блокироваться, пока другой поток не выпустит объект. Этот поток может вызываться Enter несколько раз без блокировки, однако необходимо вызвать равное количество Exit вызовов, прежде чем другие потоки, ожидающие выполнения объекта, разблокируются.

Если блокировка не была сделана из-за исключения, переменная, указанная для lockTaken параметра, будет false завершена после завершения этого метода. Это позволяет программе определять, необходимо ли освободить блокировку во всех случаях. Если этот метод возвращается без исключения, переменная, указанная для lockTaken параметра, всегда trueимеет значение, и его не нужно тестировать.

Используется Monitor для блокировки объектов (то есть ссылочных типов), а не типов значений. При передаче переменной Enterтипа значения в объект она упаковается в виде объекта. При повторном передаче той же переменной Enter она упаковается в виде отдельного объекта, и поток не блокируется. В этом случае код, который Monitor якобы защищается, не защищен. Кроме того, при передаче переменной Exitв другой отдельный объект создается. Поскольку переданный Exit объект отличается от переданного Enterв объект , Monitor вызывает SynchronizationLockExceptionисключение. Дополнительные сведения см. в разделе " Мониторы концептуального раздела".

Interrupt может прерывать потоки, ожидающие ввода Monitor объекта. Будет ThreadInterruptedException создано исключение.

Применяется к

.NET 7 и другие версии
Продукт Версии
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7
.NET Framework 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0