The Interlocked class provides methods that synchronize access to a variable that is shared by multiple threads. The threads of different processes can use this mechanism if the variable is in shared memory. Interlocked operations are atomic — that is, the entire operation is a unit that cannot be interrupted by another interlocked operation on the same variable. This is important in operating systems with preemptive multithreading, where a thread can be suspended after loading a value from a memory address, but before having the chance to alter it and store it.
The Interlocked class provides the following operations:
In the .NET Framework version 2.0, the Add method adds an integer value to a variable and returns the new value of the variable.
In the .NET Framework version 2.0, the Read method reads a 64-bit integer value as an atomic operation. This is useful on 32-bit operating systems, where reading a 64-bit integer is not ordinarily an atomic operation.
The Exchange method performs an atomic exchange of the value in a specified variable, returning that value and replacing it with a new value. In the .NET Framework version 2.0, a generic overload of this method can be used to perform this exchange on a variable of any reference type. See Exchange(Of T)(T, T).
The CompareExchange method also exchanges two values, but contingent on the result of a comparison. In the .NET Framework version 2.0, a generic overload of this method can be used to perform this exchange on a variable of any reference type. See CompareExchange(Of T)(T, T, T).
On modern processors, the methods of the Interlocked class can often be implemented by a single instruction. Thus, they provide very high-performance synchronization and can be used to build higher-level synchronization mechanisms, like spin locks.
The CompareExchange method can be used to protect computations that are more complicated than simple increment and decrement. The following example demonstrates a thread-safe method that adds to a running total stored as a floating point number. (For integers, the Add method is a simpler solution.) For complete code examples, see the overloads of CompareExchange that take single-precision and double-precision floating-point arguments (CompareExchange(Single, Single, Single) and 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
The Exchange and CompareExchange methods have overloads that take arguments of type Object. The first argument of each of these overloads is ref Object (ByRef … As Object in Visual Basic), and type safety requires the variable passed to this argument to be typed strictly as Object; you cannot simply cast the first argument to type Object when calling these methods.
The following code example shows a property of type ClassA that can be set only once, as it might be implemented in the .NET Framework version 1.0 or 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