연동 작업
업데이트: 2007년 11월
Interlocked 클래스는 여러 스레드가 공유하는 변수에 대한 액세스를 동기화하는 메서드를 제공합니다. 변수가 공유 메모리에 있으면 다른 프로세스의 스레드도 이 메커니즘을 사용할 수 있습니다. 연동 작업은 원자성을 가집니다. 즉, 전체 작업은 동일한 변수를 공유하는 다른 연동 작업에 의해 중단될 수 없는 하나의 단위입니다. 이는 선점형 다중 스레딩을 제공하는 운영 체제에서 중요합니다. 이러한 운영 체제에서 스레드는 메모리 주소로부터 값을 로드한 후 이를 변경하여 저장하기 전에 일시 중단될 수 있습니다.
Interlocked 클래스는 다음과 같은 작업을 제공합니다.
.NET Framework 버전 2.0에서 Add 메서드는 정수 값을 변수에 추가하고 변수의 새 값을 반환합니다.
.NET Framework 버전 2.0에서 Read 메서드는 64비트 정수 값을 원자 연산으로 읽습니다. 이는 32비트 운영 체제에서 유용하며 이 운영 체제에서 64비트 정수를 읽는 것은 일반적으로 원자 연산이 아닙니다.
Exchange 메서드는 값을 반환하고 이를 새 값으로 바꾸면서 지정된 변수에서 값의 원자 교환을 수행합니다. .NET Framework 버전 2.0에서 이러한 메서드의 제네릭 오버로드는 참조 형식의 변수에서 이러한 교환을 수행하는 데 사용할 수 있습니다. Exchange<T>(T%, T)을 참조하십시오.
CompareExchange 메서드는 두 값을 교환하기도 하지만 비교 결과에 따라 달라집니다. .NET Framework 버전 2.0에서 이러한 메서드의 제네릭 오버로드는 참조 형식의 변수에서 이러한 교환을 수행하는 데 사용할 수 있습니다. CompareExchange<T>(T%, T, T)을 참조하십시오.
최신 프로세서에서는 Interlocked 클래스의 메서드를 한 개의 명령으로 구현할 수도 있습니다. 따라서 이러한 메서드는 고성능 동기화를 제공하며 스핀 잠금과 같은 보다 높은 수준의 동기화 메커니즘을 빌드하는 데 사용할 수 있습니다.
Monitor 및 Interlocked 클래스를 함께 사용하는 예제를 보려면 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의 형식화되지 않은 오버로드
Exchange 및 CompareExchange 메서드에는 Object 형식의 인수를 사용하는 오버로드가 있습니다. 이러한 각 오버로드의 첫 번째 인수는 ref Object(Visual Basic에서는 ByRef ¢¢ç¦ As Object)이며 형식 안전성을 위해 이 인수로 전달되는 변수는 Object로 강력하게 형식화되어야 합니다. 사용자는 이러한 메서드를 호출할 때 첫 번째 인수를 Object 형식으로 간단하게 캐스팅할 수 없습니다.
참고: |
---|
.NET Framework 버전 2.0에서 Exchange 및 CompareExchange 메서드의 제네릭 오버로드를 사용하여 강력하게 형식화된 변수를 교환합니다. |
다음 코드 예제에서는 .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;
}
}
}