Para ver el artículo en inglés, active la casilla Inglés. También puede ver el texto en inglés en una ventana emergente si pasa el puntero del mouse por el texto.
Traducción
Inglés

Interlocked Operations

 

La clase Interlocked proporciona métodos que sincronizan el acceso a una variable compartida por varios subprocesos. Los subprocesos de diferentes procesos pueden utilizar este mecanismo si la variable está en la memoria compartida. Las operaciones de bloqueo son atómicas, es decir, el conjunto de la operación constituye una unidad que ninguna otra operación de bloqueo puede interrumpir en la misma variable. Esto último tiene importancia en sistemas operativos con multithreading preferente, donde se puede suspender un subproceso después de cargar un valor desde una dirección de memoria, pero antes de tener oportunidad de alterarlo y almacenarlo.

La clase Interlocked proporciona las siguientes operaciones:

  • En la versión 2.0 de .NET Framework, el método Add agrega un valor entero a una variable y devuelve el nuevo valor de la variable.

  • En la versión 2.0 de .NET Framework, el método Read lee un valor entero de 64 bits como una operación atómica. Esto resulta útil en sistemas operativos de 32 bits, donde leer un entero de 64 bits no constituye habitualmente una operación atómica.

  • Los métodos Increment y Decrement incrementan o decrementan una variable y devuelven el valor resultante.

  • El método Exchange realiza un intercambio atómico del valor en la variable especificada, devolviendo dicho valor y reemplazándolo por un valor nuevo. En la versión 2.0 de .NET Framework, puede utilizarse una sobrecarga genérica de este método para realizar este intercambio en una variable con cualquier tipo de referencia. Vea Exchange<T>(T, T).

  • El método CompareExchange también intercambia dos valores, pero en función del resultado de una comparación. En la versión 2.0 de .NET Framework, puede utilizarse una sobrecarga genérica de este método para realizar este intercambio en una variable con cualquier tipo de referencia. Vea CompareExchange<T>(T, T, T).

En los procesadores modernos, los métodos de la clase Interlocked a menudo pueden implementarse mediante una única instrucción. Así, proporcionan una sincronización con un rendimiento muy elevado y pueden utilizarse para compilar mecanismos de sincronización de nivel superior, como bloqueos circulares.

Para obtener un ejemplo que utiliza las clases Monitor y Interlocked en combinación, vea Monitores.

El método CompareExchange se puede utilizar para proteger cálculos más complicados que un simple incremento y decremento. En el siguiente ejemplo se muestra un método seguro para subprocesos que se agrega a un total actualizado almacenado como un número de punto flotante. (En el caso de los números enteros, el método Add constituye una solución más sencilla.) Para obtener ejemplos de código completos, vea las sobrecargas de CompareExchange que toma argumentos de punto flotante de precisión sencilla y doble (CompareExchange(Single, Single, Single) y CompareExchange(Double, Double, Double)).

using System;
using System.Threading;

public class ThreadSafe
{
    // totalValue contains a running total that can be updated
    // by multiple threads. It must be protected from unsynchronized
    // access.
    private double totalValue = 0;

    // The Total property returns the running total.
    public double Total
    {
        get { return totalValue; }
    }

    // AddToTotal safely adds a value to the running total.
    public double AddToTotal(double addend)
    {
        double initialValue, computedValue;
        do
        {
            // Save the current running total in a local variable.
            initialValue = totalValue;

            // Add the new value to the running total.
            computedValue = initialValue + addend;

            // CompareExchange compares totalValue to initialValue. If
            // they are not equal, then another thread has updated the
            // running total since this loop started. CompareExchange
            // does not update totalValue. CompareExchange returns the
            // contents of totalValue, which do not equal initialValue,
            // so the loop executes again.
        }
        while (initialValue != Interlocked.CompareExchange(
            ref totalValue, computedValue, initialValue));
        // If no other thread updated the running total, then
        // totalValue and initialValue are equal when CompareExchange
        // compares them, and computedValue is stored in totalValue.
        // CompareExchange returns the value that was in totalValue
        // before the update, which is equal to initialValue, so the
        // loop ends.

        // The function returns computedValue, not totalValue, because
        // totalValue could be changed by another thread between
        // the time the loop ends and the function returns.
        return computedValue;
    }
}

Los métodos Exchange y CompareExchange disponen de sobrecargas que aceptan argumentos de tipo Object. El primer argumento de cada una de estas sobrecargas es ref Object (ByRef … As Object en Visual Basic) y la seguridad de tipos requiere que se pase la variable a este argumento para que sea estrictamente de tipo Object; no se puede transformar simplemente el primer argumento a tipo Object cuando se realiza la llamada a estos métodos.

System_CAPS_noteNota

En la versión 2.0 de .NET Framework, utilice las sobrecargas genéricas de los métodos Exchange y CompareExchange para intercambiar variables fuertemente tipadas.

En el siguiente ejemplo de código se muestra una propiedad de tipo ClassA que sólo puede establecerse una vez, ya que debería implementarse en la versión 1.0 o 1.1 de .NET Framework.

public class ClassB
{
    // The private field that stores the value for the
    // ClassA property is intialized to null. It is set
    // once, from any of several threads. The field must
    // be of type Object, so that CompareExchange can be
    // used to assign the value. If the field is used
    // within the body of class Test, it must be cast to
    // type ClassA.
    private object classAValue = null;

    // This property can be set once to an instance of
    // ClassA. Attempts to set it again cause an
    // exception to be thrown.
    public ClassA ClassA
    {
        get
        {
            return (ClassA) classAValue;
        }

        set
        {
            // CompareExchange compares the value in classAValue
            // to null. The new value assigned to the ClassA
            // property, which is in the special variable 'value',
            // is placed in classAValue only if classAValue is
            // equal to null.
            if (null != Interlocked.CompareExchange(ref classAValue,
                (Object) value, null))
            {
                // CompareExchange returns the original value of
                // classAValue; if it is not null, then a value
                // was already assigned, and CompareExchange did not
                // replace the original value. Throw an exception to
                // indicate that an error occurred.
                throw new ApplicationException("ClassA was already set.");
            }
        }
    }
}
Mostrar: