Ler en inglés

Compartir por


Monitor.Enter Método

Definición

Adquiere un bloqueo exclusivo en un objeto especificado.

Sobrecargas

Enter(Object)

Adquiere un bloqueo exclusivo en el objeto especificado.

Enter(Object, Boolean)

Adquiere un bloqueo exclusivo en el objeto especificado y establece de forma atómica un valor que indica si se realizó el bloqueo.

Enter(Object)

Source:
Monitor.cs
Source:
Monitor.cs
Source:
Monitor.cs

Adquiere un bloqueo exclusivo en el objeto especificado.

C#
public static void Enter (object obj);

Parámetros

obj
Object

Objeto en el que se va a adquirir el bloqueo de monitor.

Excepciones

El parámetro obj es null.

Ejemplos

En el siguiente ejemplo se muestra cómo se utiliza el método Enter.

C#
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
 */

Comentarios

Use Enter para adquirir en Monitor el objeto pasado como parámetro . Si otro subproceso ha ejecutado en Enter el objeto pero aún no ha ejecutado el correspondiente Exit, el subproceso actual se bloqueará hasta que el otro subproceso libere el objeto. Es legal que el mismo subproceso invoque Enter más de una vez sin bloquearlo; sin embargo, se debe invocar un número igual de llamadas antes de Exit que otros subprocesos que esperan en el objeto se desbloqueen.

Se usa Monitor para bloquear objetos (es decir, tipos de referencia), no tipos de valor. Cuando se pasa una variable de tipo de valor a Enter, se conversión boxed como un objeto . Si pasa la misma variable a Enter otra, se aplica la conversión boxing como un objeto independiente y el subproceso no se bloquea. En este caso, el código que Monitor supuestamente protege no está protegido. Además, cuando se pasa la variable a Exit, se crea otro objeto independiente. Dado que el objeto pasado a Exit es diferente del objeto pasado a Enter, Monitor produce SynchronizationLockException. Para obtener más información, consulte el tema conceptual Monitores.

Interrupt puede interrumpir los subprocesos que están esperando entrar en un Monitor objeto . ThreadInterruptedException Se producirá una excepción .

Usar un C# try... finallyblock (Try...Finally en Visual Basic) para asegurarse de liberar el monitor o usar la instrucción de C# lock (SyncLock instrucción en Visual Basic), que encapsula los Enter métodos y Exit en ...try finally Bloquear.

Consulte también

Se aplica a

.NET 9 e outras versións
Produto Versións
.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, 8, 9
.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, 4.8.1
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0

Enter(Object, Boolean)

Source:
Monitor.CoreCLR.cs
Source:
Monitor.CoreCLR.cs
Source:
Monitor.CoreCLR.cs

Adquiere un bloqueo exclusivo en el objeto especificado y establece de forma atómica un valor que indica si se realizó el bloqueo.

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

Parámetros

obj
Object

Objeto en el que se va a esperar.

lockTaken
Boolean

Resultado del intento de adquirir el bloqueo, pasado por referencia. La entrada debe ser false. El resultado es true si se adquiere el bloqueo; en caso contrario, el resultado es false. El resultado se establece aunque se produzca una excepción durante el intento de adquirir el bloqueo.

Nota Si no se produce ninguna excepción, la salida de este método siempre truees .

Excepciones

La entrada a lockTaken es true.

El parámetro obj es null.

Ejemplos

En el código siguiente se muestra el patrón básico para usar la sobrecarga del Enter(Object, Boolean) método . Esta sobrecarga siempre establece el valor de la variable que se pasa al ref parámetro (ByRef en Visual Basic), lockTakenincluso si el método produce una excepción, por lo que el valor de la variable es una manera confiable de probar si se debe liberar el bloqueo.

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

Comentarios

Use Enter para adquirir en Monitor el objeto pasado como obj parámetro . Si otro subproceso ha ejecutado en Enter el objeto pero aún no ha ejecutado el correspondiente Exit, el subproceso actual se bloqueará hasta que el otro subproceso libere el objeto. Es legal que el mismo subproceso invoque Enter más de una vez sin bloquearlo; sin embargo, se debe invocar un número igual de llamadas antes de Exit que otros subprocesos que esperan en el objeto se desbloqueen.

Si no se tomó el bloqueo porque se produjo una excepción, la variable especificada para el lockTaken parámetro es false después de que finalice este método. Esto permite al programa determinar, en todos los casos, si es necesario liberar el bloqueo. Si este método devuelve sin iniciar una excepción, la variable especificada para el lockTaken parámetro siempre truees y no es necesario probarlo.

Se usa Monitor para bloquear objetos (es decir, tipos de referencia), no tipos de valor. Cuando se pasa una variable de tipo de valor a Enter, se conversión boxed como un objeto . Si pasa la misma variable a Enter otra, se aplica la conversión boxing como un objeto independiente y el subproceso no se bloquea. En este caso, el código que Monitor supuestamente protege no está protegido. Además, cuando se pasa la variable a Exit, se crea otro objeto independiente. Dado que el objeto pasado a Exit es diferente del objeto pasado a Enter, Monitor produce SynchronizationLockException. Para obtener más información, consulte el tema conceptual Monitores.

Interrupt puede interrumpir los subprocesos que están esperando entrar en un Monitor objeto . ThreadInterruptedException Se producirá una excepción .

Se aplica a

.NET 9 e outras versións
Produto Versións
.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, 8, 9
.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, 4.8.1
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0