Interlocked-Vorgänge

Aktualisiert: November 2007

Die Interlocked-Klasse stellt Methoden bereit, die den Zugriff auf eine Variable synchronisieren, die von mehreren Threads gemeinsam genutzt wird. Threads aus verschiedenen Prozessen können diesen Mechanismus verwenden, wenn sich die Variable im freigegebenen Speicher befindet. Interlocked-Vorgänge sind unteilbare (atomarische) Vorgänge. Der gesamte Vorgang ist eine Einheit, die durch einen anderen Interlocked-Vorgang mit derselben Variablen nicht unterbrochen werden kann. Dies ist in Betriebssystemen mit präemptivem Multithreading von Bedeutung, bei denen ein Thread nach dem Laden eines Werts aus einer Speicheradresse unterbrochen werden kann, bevor der Wert geändert oder gespeichert wurde.

Die Interlocked-Klasse stellt die folgenden Vorgänge bereit:

  • In .NET Framework, Version 2.0, wird einer Variablen durch die Add-Methode ein ganzzahliger Wert hinzugefügt und der neue Wert der Variablen zurückgegeben.

  • In .NET Framework, Version 2.0, liest die Read-Methode einen 64-Bit-Ganzzahlwert in einem atomaren Vorgang. Dies ist von Vorteil für 32-Bit-Betriebssysteme, bei denen das Lesen eines 64-Bit-Ganzzahlwerts normalerweise keinen atomaren Vorgang darstellt.

  • Die Increment-Methode und die Decrement-Methode inkrementieren oder dekrementieren eine Variable und geben den daraus resultierenden Wert zurück.

  • Die Exchange-Methode führt einen atomaren Werteaustausch in einer bestimmten Variablen aus. Dabei wird dieser Wert zurückgegeben und durch einen neuen Wert ersetzt. In .NET Framework, Version 2.0, kann eine generische Überladung dieser Methode verwendet werden, um diesen Austausch für eine Variable eines beliebigen Referenztyps auszuführen. Siehe Exchange<T>(T%, T).

  • Auch von der CompareExchange-Methode werden zwei Werte ausgetauscht, und zwar abhängig vom Ergebnis eines Vergleichs. In .NET Framework, Version 2.0, kann eine generische Überladung dieser Methode verwendet werden, um diesen Austausch für eine Variable eines beliebigen Referenztyps auszuführen. Siehe CompareExchange<T>(T%, T, T).

Bei modernen Prozessoren können die Methoden der Interlocked-Klasse oft durch eine einzige Anweisung implementiert werden. Sie bieten folglich eine Hochleistungssynchronisierung und können verwendet werden, um Mechanismen für höherstufige Synchronisierung zu erstellen (z. B. Spinlocks).

Ein Beispiel, in dem die Monitor-Klasse und die Interlocked-Klasse zusammen verwendet werden, finden Sie unter Monitore.

CompareExchange-Beispiel

Mit der CompareExchange-Methode können Sie Berechnungen schützen, die komplexer sind als einfaches Inkrementieren und Dekrementieren. Im folgenden Beispiel wird eine threadsichere Methode dargestellt, durch die einer als Gleitkommazahl gespeicherten laufenden Summe Werte hinzugefügt werden. (Bei ganzen Zahlen empfiehlt sich die Verwendung der Add-Methode.) Ausführliche Codebeispiele finden Sie in den Überladungen von CompareExchange, die Gleitkommaargumente mit einfacher und doppelter Genauigkeit akzeptieren (CompareExchange(Single%, Single, Single) und CompareExchange(Double%, Double, Double)).

Imports System.Threading

Public Class ThreadSafe
    ' Field totalValue contains a running total that can be updated
    ' by multiple threads. It must be protected from unsynchronized 
    ' access.
    Private totalValue As Double = 0.0

    ' The Total property returns the running total.
    Public ReadOnly Property Total As Double
        Get
            Return totalValue
        End Get
    End Property

    ' AddToTotal safely adds a value to the running total.
    Public Function AddToTotal(ByVal addend As Double) As Double
        Dim initialValue, computedValue As Double
        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.
        Loop While initialValue <> Interlocked.CompareExchange( _
            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
    End Function
End Class
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;
    }
}

Nicht typisierte Überladungen von Exchange und CompareExchange

Die Exchange-Methode und die CompareExchange-Methode verfügen über Überladungen, die Argumente des Typs Object akzeptieren. Das erste Argument für jede dieser Überladungen ist ref Object (ByRef … As Object in Visual Basic). Die Typsicherheit erfordert, dass die an dieses Argument übergebene Variable strikt als Object typisiert sein muss. Sie können beim Aufruf dieser Methoden das erste Argument nicht ohne weiteres in den Typ Object umwandeln.

Hinweis:

Verwenden Sie in .NET Framework, Version 2.0, die generischen Überladungen der Exchange-Methode und der CompareExchange-Methode, um stark typisierte Variablen auszutauschen.

Das folgende Codebeispiel enthält eine Eigenschaft vom Typ ClassA, die nur einmal festgelegt werden kann, da sie möglicherweise in .NET Framework, Version 1.0 oder 1.1, implementiert wurde.

Public Class ClassB
    ' The private field that stores the value for the
    ' ClassA property is intialized to Nothing. 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 classAValue As [Object] = Nothing
    ' This property can be set once to an instance of 
    ' ClassA. Attempts to set it again cause an
    ' exception to be thrown.
    
    Public Property ClassA() As ClassA
        Set
            ' CompareExchange compares the value in classAValue
            ' to Nothing. 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 Nothing.
            If Not (Nothing Is Interlocked.CompareExchange(classAValue, _
                    CType(value, [Object]), Nothing)) Then
                ' CompareExchange returns the original value of 
                ' classAValue; if it was not Nothing, 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.")
            End If
        End Set
        Get
            Return CType(classAValue, ClassA)
        End Get
    End Property
End Class ' ClassB
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 {
        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.");
            }
        }
        get {
            return (ClassA) classAValue;
        }
    }
}

Siehe auch

Referenz

Interlocked

Monitor

Weitere Ressourcen

Verwaltetes Threading

Threadingobjekte und -features