Поделиться через


Блокируемые операции

Обновлен: Ноябрь 2007

Класс Interlocked содержит методы для синхронизации доступа к переменной, которая совместно используется несколькими потоками. Если переменная находится в общей памяти, этот механизм может использоваться потоками различных процессов. Блокируемые операции являются атомарными. Это значит, что вся операция является неделимой и не может прерываться другой блокируемой операцией с той же переменной. Это важно в операционных системах, использующих многопоточность с вытеснением, где поток может быть приостановлен после того, как он загрузит значение из адреса памяти, но до того, как он получит возможность изменить это значение и сохранить в памяти.

Класс Interlocked выполняет следующие операции.

  • В платформе .NET Framework версии 2.0 метод Add добавляет к переменной целочисленное значение и возвращает новое значение переменной.

  • В платформе .NET Framework версии 2.0, метод Read считывает 64-разрядное целочисленное значение в ходе атомарной операции. Это полезно в 32-разрядных операционных системах, где считывание 64-разрядного целого числа обычно не является атомарной операцией.

  • Методы Increment и Decrement увеличивают или уменьшают переменную и возвращают результирующее значение.

  • Метод Exchange выполняет атомарный обмен значений для указанной переменной, возвращая текущее значение и заменяя его новым значением. Универсальная перегружаемая версия этого метода в платформе .NET Framework версии 2.0 может использоваться для обмена значений в переменной любого ссылочного типа. См. описание метода Exchange<T>(T%, T).

  • Метод CompareExchange также обменивает два значения, но делает это в зависимости от результата сравнения. Универсальная перегружаемая версия этого метода в платформе .NET Framework версии 2.0 может использоваться для обмена значений в переменной любого ссылочного типа. См. описание метода CompareExchange<T>(T%, T, T).

Современные процессоры часто позволяют реализовать методы класса Interlocked одной инструкцией. Поэтому они обеспечивают очень высокую производительность синхронизации и позволяют построить механизмы синхронизации более высокого уровня, такие как спин-блокировка.

Пример, использующий сочетание классов Monitor и Interlocked, см. в разделе Мониторы.

Пример CompareExchange

Метод CompareExchange служит для защиты более сложных вычислений, чем простое увеличение и уменьшение значений. В следующем примере показан потокобезопасный метод, который добавляет значение к промежуточной сумме, хранящейся в виде числа с плавающей запятой. (Для целочисленных значений более простым решением будет метод Add.) Полные примеры кода см. в описании перегруженных версий метода CompareExchange, которые принимают аргументы с плавающей запятой одинарной и двойной точности (CompareExchange(Single%, Single, Single) и 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;
    }
}

Нетипизированные перегруженные версии методов Exchange и CompareExchange

Методы Exchange и CompareExchange имеют перегруженные версии, принимающие аргументы типа Object. Первый аргумент в каждой из этих версий имеет тип ref Object (ByRef … As Object в Visual Basic), и для безопасности типов необходима строгая типизация переменной, передаваемой этому аргументу, по типу Object. При вызове этих методов нельзя просто привести первый аргумент к типу Object.

sbhbke0y.alert_note(ru-ru,VS.90).gifПримечание.

В платформе .NET Framework версии 2.0 для обмена строго типизированными переменными используются универсальные перегружаемые версии методов Exchange и CompareExchange.

В следующем примере кода показано свойство типа ClassA, которое может задаваться только один раз, и его реализация в платформе .NET Framework версии 1.0 или 1.1.

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

См. также

Ссылки

Interlocked

Monitor

Другие ресурсы

Управляемая поточность

Объекты и функциональные возможности работы с потоками