.NET Interlocked Operations
System.Threading.Interlocked Class
In a multithreaded application, synchronization is key to protect data integrity. There are numerous ways to accomplish data synchronization in .NET and C#, such as the commonly used System.Threading.Monitor
.NET Framework class and corresponding C# lock
statement. But, when it comes to superfast synchronization, nothing beats the interlocked operations for a few simple operations:
 Math Operations
 Add 1 to a value (Increment)
 Subtract 1 from a value (Decrement)
 Add Two Values (Add)
 Get / Set Operations
 Get a value (Read)
 Get the current value and set a new value (Exchange)
 Get the current value and set a new value ifandonly if the current value is a specific value (CompareExchange)
This article first looks at why atomic operations are needed, and then goes into the various interlocked operations accessible via the System.Threading.Interlocked
class.
Why Interloced Operations Are Needed
Simply put, an interlocked operation is an operation that executes without interruption. An interlocked operation generally consists of a sequence of two or more steps that affect state information or rely on consistency in state information and need to be executed in order, without interruption, to ensure the integrity of that state information. If the execution sequence of the operation is interrupted, it is possible that the state information can be adversely affected, and interlocked operations ensure that that does not happen.
The Need for Interlocked Math Operations
Experienced programmers generally already know this fact, but novice programmers are often surprised to find out that something as simple as x++
is not atomic! For example, the simple x++
executes the following sequence of steps, but does not prevent interruption between the steps or manipulation of the value from another thread:
 Read the current value of the variable
x
 Calculate the new value to be 1 + the current value
 Write the new value back into the variable
x
Since x++
is nonatomic, if code running on another thread also tries to increment the variable x
at the same time via x++
, the variable x
could end up with the wrong value as is indicated by one possible outcome execution outcome below:
Thread 1: x++ (nonatomic)  Thread 2: x++ (nonatomic) 

Read value 100 from x  
Calculate the new value to be 101  
Read value 100 from x  
Write value 101 back to x  
Calculate the new value to be 101  
Write value 101 back to x 
Thus, as you can see from the example above, even though x++
was called twice, the final value residing in x
is incorrect because it is 101 instead of 102.
But, if the ++
operator was atomic (which it is not!), then it would be guaranteed that calling x++
concurrently from two different threads would result in the correct value of 102 because the read, calculate, write sequences could not be interrupted on either thread:
Thread 1: x++ (if atomic)  Thread 2: x++ (if atomic) 

Read value 100 from x  
Calculate the new value to be 101  
Write value 101 back to x  
Read value 101 from x  
Calculate the new value to be 102  
Write value 102 back to x 
From this example of x++
, it should be pretty obvious that similar problems would exist for x
or x + y
.
Solutions to these problems are provided by
Interlocked.Increment()
Interlocked.Decrement()
Interlocked.Add()
The Need for Interlocked Get and Set Operations
In addition to needing interlocked math operations as was mentioned above, there is also a need for interlocked get and set methods.
Get on 32Bit vs 64Bit Machines
In computer architecture, a "word" is always one "native width" worth of bits. On an 8bit processor a word is 8bits, on a 16bit processor a word is 16bits, on a 32bit processor a word is 32bits, and so on. To eliminate confusion for people that may be accustom to a word being exactly 2bytes (16bits) I will used the terminology "word8", "word16", "word32", "word64", etc. when referring to a word of a specific size, or "wordN" to refer to a word that is the native or natural size based on the processor's architecture.
.NET can run on 32bit and 64bit machines. And, in .NET, each value is at least one wordN wide. For example, even a bool value  which technically needs only 1bit in which to store its entire state information  is stored in a word32 on a 32bit machine or in a word64 on a 64bit machine. In other words, .NET does not "pack" the data to optimize memory usage.
The .NET Framework supports a wide variety of numeric types. For example, consider what it means to have a System.Int32
( int
in C#) and System.Int64
(long
in C#) on both 32bit and 64bit machines.
Data Type  C# Alias  Data Size  .NET 32bit  .NET 64bit 

System.Int32  int  32bits  1 x word32  1 x word64 
System.Int64  long  64bits  2 x word32  1 x word64 
From that table, note that on a 64bit machine a long
is stored in only one wordN (where wordN = word64), but on a 32bit machine a long
is stored in two wordsN (where wordN = word32). This creates an interesting problem: In one CPU clock cycle, the CPU can typically only move one wordN from memory to CPU register, or from a CPU register to memory. Therefore, when a long
is used on a 32bit machine where it takes 2 wordsN to hold all 64bits, it also takes 2 CPU clock cycles to move the value.
Moving one wordN from memory to register is atomic  nothing is going to interrupt that. But, moving two wordsN from memory to register is not atomic! That means a thread context switch could occur between moving the lower 32bits and upper 32bits, which also means that another thread could change the value in between those two steps in that operation sequence. In multiprocessor and muticore environmets, it might not even take a thread context switch:
Thread 1 (Get 64bit value on 32bit machine)  Thread 2 (Set 64bit value on 32bit machine) 

Get lower 32bits of x 

Set lower 32bits of x 

Set upper 32bits of x 

Get upper 32bits of x 
Doh! That table above shows that Thread 1 just got the lower 32bits from the original value of x
but the upper 32bits from the new value of x
that was just set by Thread 2. In other words, it did not get either the old value or the new value, but instead some hybrid value.
Thus, as you can see, even for valuetypes such as a long
it becomes necessary to synchronize access when reading and writing across thread boundaries.
A solution to this problem is provided by
Interlocked.Read()
Set a New Value, or Swap Two Values
For a similar 32bit / 64bit reason as was discussed in the previous section on getting a value, it is also necessary to synchronize when setting a 64bit value on a 32bit machine.
Thread 1 (Set 64bit value on 32bit machine)  Thread 2 (Set 64bit value on 32bit machine) 

Set lower 32bits of x 

Set lower 32bits of x 

Set upper 32bits of x 

Set upper 32bits of x 
As you can set from that example, if the set is not atomic the final value in x
could be the lower 32bits set from Thread 2 but the upper 32bits set from Thread 1. In that case, the final value is neither the value set by Thread 1 nor the value set but Thread 2, and instead a hybrid of the two.
In addition to setting a 64bit value on a 32bit processor, there is also a reason to need to synchronize a set even if the value is a 32bit value on a 32bit process where the entire value gets moved in a single CPU clock cycle: without synchronization, it would not be possible to set a new value and return the existing value with 100% certainty that the value that is returned is actually the value that was replaced:
 Get existing value and store it as original value
 Set new value
 Return original value
As an example, assume variable x
is a bool
and the code needs to turn the flag on and know that it is indeed the code that caused the flag to turn on. Without some kind of synchronization, the following situation could occur if two different threads try to set the flag at the same time.
bool y = x;
x = true;
return y;
Thread 1 (Set new value, return original value)  Thread 2 (Set new value, return original value) 

Get existing value of x (false) 

Get existing value of x (false) 

Set new value of x (true) 

Return original value of x (false) 

Set current value of x (true) 

Return original value of x (false) 
As you can see from that example, both Thread 1 and Thread 2 now think they were the code that caused the flag to turn on because they were each told that the original value was false, and they each also know they just set the flag to true.
A solution to this problem is provided by
Interlocked.Exchange()
Set New Value If Existing Value Is Something
It is often necessary to change the existing value to a new value ifandonlyif that exiting value is a specific value. Logically, that looks something like this:
// If x is 100, then set it to 200
if (x == 100)
{
x = 200;
}
By now, if you're still reading this post, it's probably pretty obvious that threading can cause unintended side effects with that code. For example, it's easy to imagine that x
was indeed 100
at the time it was checked in the conditional, but that another thread could have already changed x
to a different value by the time x
is set to 200
.
Therefore, if x
is something that can be changed by multiple threads, synchronization would be required.
A solution to this problem is provided by
Interlocked.CompareExchange()
System.Threading.Interlocked Class
Fortunately, the .NET Framework provides highspeed, interlocked operations for the various situations described above as part of the System.Thread.Interlocked
class. These methods follow a common set of rules:
 The first parameter of an
Interlocked
method is always theref
parameter that is the variable holding the value that is to be read and/or changed. Since it is aref
parameter, it must be a variable or class field, and cannot be a property.  To make the implementation as fast as possible, the
Interlocked
methods do not throw exceptions.  The
Interlocked
math operations (Increment()
,Decrement()
,Add()
) return the new value (i.e. value after the math operation has completed).  The
Interlocked
get and set operations (Read()
,Exchange()
,CompareExchange()
) return the original value (i.e. before the new value was set, if applicable).  The
Interlocked
methods are synchronized with each other, but are not synchronized with other .NET operators or synchronization classes. For example doing anInterlocked.Increment()
on Thread 1 at the same time as a++
on the same variable from Thread 2 is not safe. Likewise, doing anInterlocked.Exchange()
to set a value from outside of aMonitor
lock while another thread is using a=
assignment operator from inside aMonitor
lock is not safe.
Interlocked Math Operations
The three interlocked math methods have overloads for Int32
and Int64
. They execute the math operation, and return the new value without throwing any exceptions. In order to increase execution speed, the Interlocked
math operations do not check for overflows and underflows.
Two'sComplement Integer Overflows and Underflows
The Interlocked
class is CLS (Common Language Specification) compliant, so it supports signed integer values but does not support unsigned integer values. Since the Interlocked
math operations do not detect overflows and underflows, it is useful to understand how integer values are stored in two'scomplement format so that you know what to expect when values are incremented or decremented, or when two signed integers are added.
The table below serves as a reminder about how signed integer values are stored in two'scomplement format for both Int32
and Int64
types. The table shows the values form all bits off, to all bits on.
Value  System.Int32  System.Int64 

0  0x00000000 
0x0000000000000000 
1  0x00000001 
0x0000000000000001 
2  0x00000002 
0x0000000000000002 
...  ...  ... 
Max Value  0x7FFFFFFF (+2,147,483,647) 
0x7FFFFFFFFFFFFFFF (+9,223,372,036,854,775,807) 
Min Value  0x80000000 (2,147,483,648) 
0x8000000000000000 (9,223,372,036,854,775,808) 
Min Value + 1  0x80000001 (2,147,483,647) 
0x8000000000000001 (9,223,372,036,854,775,807) 
Min Value + 2  0x80000002 (2,147,483,646) 
0x8000000000000002 (9,223,372,036,854,775,806) 
...  ...  ... 
3  0xFFFFFFFD 
0xFFFFFFFFFFFFFFFD 
2  0xFFFFFFFE 
0xFFFFFFFFFFFFFFFE 
1  0xFFFFFFFF 
0xFFFFFFFFFFFFFFFF 
In two'scomplement format, to convert a positive value to a negative value, or viceversa, you simply
Invert and add one
In that phrase, "inverting" simply means flipping the state of all the bits, which is also known as taken the "one'scomplement". The two tables below show that the technique works for both positive and negative numbers.
Unsigned Value  Signed Value  Binary Value (16bit)  Description 

1  1  0000000000000001 
One 
65534  2  1111111111111110 
"Invert" (One'scomplement of 1) 
65535  1  1111111111111111 
"Add One" (Two'scomplement of 1) 
Unsigned Value  Signed Value  Binary Value (16bit)  Description 

65535  1  1111111111111111 
Negative One 
0  0  0000000000000000 
"Invert" (One'scomplement of 1) 
1  1  0000000000000001 
"Add One" (Two'scomplement of 1) 
Knowing that the values simply wraparound instead of checking for overflow and underflow, and referring to the tables above, you can see that the following will happen.
Operation  With Overflow/Underflow  Without Overflow/Underflow 

Decrementing from 0  1  1 
Incrementing from Max Value  Overflow  MinValue 
Decrementing from MinValue  Underflow  MaxValue 
Incrementing from 1  0  0 
Additionally, when adding two signed integers...
 If you add a positive number and a negative number together, the answer cannot result in an overflow or underflow
 If you add two positive numbers together, the answer might result in an overflow
 If you add two negative number together, the answer might result in an underflow
Interlocked.Increment() and Interlocked.Decrement()
The Interlocked.Increment()
and Interlocked.Decrement()
are complementary operations. As their names imply, they simply increment of decrement value. Both have overloads for Int32
and Int64
:
// Add 1 to the current value of an Int32, returning the new value
// Wrap around instead of throwing an OverflowException
public static int Increment(
ref int value
)
// Add 1 to the current value of an Int64, returning the new value
// Wrap around instead of throwing an OverflowException
public static long Increment(
ref long value
)
// Subtract 1 from the current value of an Int32, returning the new value
// Wrap around instead of throwing an UnderflowException
public static int Decrement(
ref int value
)
// Subtract 1 from the current value of an Int64, returning the new value
// Wrap around instead of throwing an UnderflowException
public static long Decrement(
ref long value
)
There are numerous reasons why someone might want to increment an integer. One of the common reasons ins reference counting.
Interlocked.Add()
The Interlocked.Add()
function adds two Int32
or two Int64
numbers, store it in the specified variable, and return the resulting value. Each of the numbers may be positive or negative, and the the resulting is wrapped around instead of throwing an OverflowException
or UnderflowException
.
// Add two Int32 numbers (addend1 and addend2)
// Store the calculated result in value
// Return the calculated result
// Wrap around instead of throwing OverflowException or UnderflowException
public static int Add(
ref int value,
int addend1,
int addedn2)
// Add two Int64 numbers (addend1 and addend2)
// Store the calculated result in value
// Return the calculated result
// Wrap around instead of throwing OverflowException or UnderflowException
public static Int64 Add(
ref long value,
long addend1,
long addedn2)
Interlocked Get and Set Operations
The need for the interlocked read, exchange, and compareexchange functionality was described a few sections above. This section describes the methods that .NET provides for accomplishing these tasks.
Interlocked.Read()
.NET runs on 32bit and 64bit machines. Neither 32bit nor 64bit machines would have an issue reading a 32bit number in one CPU cycle. But, a 32bit machine cannot read a 64bit number in one CPU cycle. Therefore, there is no reason for a 32bit interlocked read operation,but is a need for a 64bit interlocked read operation.
You can use the same method below from both 32bit machines and 64bit machines, and the compiler is smart enough to know that it needs to do something for 32bit machines, but does not need to do anything for 64bit machines.
// Read 64bit value in way that is "safe" from corruption by other Interlocked operations
// This provides no protection against nonInterlocked operations, such as x++
public static long Read(
ref long value)
Interlocked.Exchange()
The Interlocked.Exchange()
method writes a new value, returning the original value. There are a variety of different overloads of this for 32bit and 64bit data types, including:
double (System.Double)
int (System.Int32)
long (System.Int64)
IntPtr (System.IntPtr)
object (System.Object)
float
double
 T
The Interlocked.Exchange always sets the new value, and always returns the original value.
Remember the simple example of turning a flag on and trying to determine which code actually turned it on?
bool y = x;
x = true;
return y;
That can easily be done with an Interlocked.Exchange:
var y = Interlocked.Exchange(ref x, true);
return y;
Interlocked.CompareExchange()
This method is sets a new value ifandonlyif the current value is the specified value. As one would expect, it has the same kinds of overloads as the Interlocked.Excahnge disussed above.
Remember the simple example of trying to set the value to 200 ifandonlyif the current value is 100?
// If x is 100, then set it to 200
if (x == 100)
{
x = 200;
}
That can easily be accomplished with Interlocked.CompareExchange:
// Set x to 200 if and only if the original value is 100, and return the original value
if (100 == Interlocked.CompareExchange(ref x, 200, 100))
{
// If you make it here, the value of x was 100 and has just changed to 200
}
Programmer, Engineer