다음을 통해 공유


연동 작업

업데이트: 2007년 11월

Interlocked 클래스는 여러 스레드가 공유하는 변수에 대한 액세스를 동기화하는 메서드를 제공합니다. 변수가 공유 메모리에 있으면 다른 프로세스의 스레드도 이 메커니즘을 사용할 수 있습니다. 연동 작업은 원자성을 가집니다. 즉, 전체 작업은 동일한 변수를 공유하는 다른 연동 작업에 의해 중단될 수 없는 하나의 단위입니다. 이는 선점형 다중 스레딩을 제공하는 운영 체제에서 중요합니다. 이러한 운영 체제에서 스레드는 메모리 주소로부터 값을 로드한 후 이를 변경하여 저장하기 전에 일시 중단될 수 있습니다.

Interlocked 클래스는 다음과 같은 작업을 제공합니다.

  • .NET Framework 버전 2.0에서 Add 메서드는 정수 값을 변수에 추가하고 변수의 새 값을 반환합니다.

  • .NET Framework 버전 2.0에서 Read 메서드는 64비트 정수 값을 원자 연산으로 읽습니다. 이는 32비트 운영 체제에서 유용하며 이 운영 체제에서 64비트 정수를 읽는 것은 일반적으로 원자 연산이 아닙니다.

  • IncrementDecrement 메서드는 변수를 증가 또는 감소시킨 다음 결과 값을 반환합니다.

  • Exchange 메서드는 값을 반환하고 이를 새 값으로 바꾸면서 지정된 변수에서 값의 원자 교환을 수행합니다. .NET Framework 버전 2.0에서 이러한 메서드의 제네릭 오버로드는 참조 형식의 변수에서 이러한 교환을 수행하는 데 사용할 수 있습니다. Exchange<T>(T%, T)을 참조하십시오.

  • CompareExchange 메서드는 두 값을 교환하기도 하지만 비교 결과에 따라 달라집니다. .NET Framework 버전 2.0에서 이러한 메서드의 제네릭 오버로드는 참조 형식의 변수에서 이러한 교환을 수행하는 데 사용할 수 있습니다. CompareExchange<T>(T%, T, T)을 참조하십시오.

최신 프로세서에서는 Interlocked 클래스의 메서드를 한 개의 명령으로 구현할 수도 있습니다. 따라서 이러한 메서드는 고성능 동기화를 제공하며 스핀 잠금과 같은 보다 높은 수준의 동기화 메커니즘을 빌드하는 데 사용할 수 있습니다.

MonitorInterlocked 클래스를 함께 사용하는 예제를 보려면 Monitor를 참조하십시오.

CompareExchange 예제

CompareExchange 메서드를 사용하여 단순한 증가 및 감소보다 훨씬 더 복잡한 계산을 보호할 수 있습니다. 다음 예제에서는 부동 소수점 숫자로 저장되는 누계에 추가되는 스레드로부터 안전한 메서드를 보여 줍니다. 정수의 경우 Add 메서드가 더 간단한 솔루션입니다. 전체 코드 예제에 대해서는 단정밀도 및 배정밀도 부동 소수점 인수(CompareExchange(Single%, Single, Single)CompareExchange(Double%, Double, Double))를 갖는 CompareExchange의 오버로드를 참조하십시오.

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의 형식화되지 않은 오버로드

ExchangeCompareExchange 메서드에는 Object 형식의 인수를 사용하는 오버로드가 있습니다. 이러한 각 오버로드의 첫 번째 인수는 ref Object(Visual Basic에서는 ByRef ¢¢ç¦ As Object)이며 형식 안전성을 위해 이 인수로 전달되는 변수는 Object로 강력하게 형식화되어야 합니다. 사용자는 이러한 메서드를 호출할 때 첫 번째 인수를 Object 형식으로 간단하게 캐스팅할 수 없습니다.

참고:

.NET Framework 버전 2.0에서 ExchangeCompareExchange 메서드의 제네릭 오버로드를 사용하여 강력하게 형식화된 변수를 교환합니다.

다음 코드 예제에서는 .NET Framework 버전 1.0 또는 1.1에서 구현될 수 있는 것처럼 한 번만 설정될 수 있는 ClassA 형식의 속성을 보여 줍니다.

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

기타 리소스

관리되는 스레딩

스레딩 개체 및 기능