Class System.Threading.Interlocked

Provides methods that support an atomic, hardware-based thread-safe operation that accesses a variable passed by reference.

Summary

The Interlocked class provides several static methods that perform simple atomic operations on variables. These operations are thread-safe and are typically implemented using hardware instructions, making them very efficient. They are crucial for implementing lock-free data structures and for managing shared resources in multithreaded applications without the overhead of traditional locking mechanisms like mutexes or semaphores.

Common operations include incrementing, decrementing, adding, exchanging values, and comparing and swapping values. These methods ensure that no other thread can interfere with the operation while it is being performed.

Remarks

When dealing with concurrent programming, ensuring that operations on shared data are thread-safe is paramount. While locks (like Monitor or Mutex) provide a straightforward way to protect shared resources, they can introduce performance bottlenecks due to context switching and potential deadlocks. The Interlocked class offers a lower-level, more performant alternative for specific atomic operations.

For example, instead of using a lock to increment a counter, you can use Interlocked.Increment(). This single operation is guaranteed to be atomic, meaning it completes entirely without interruption from other threads. Similarly, Interlocked.CompareExchange() is fundamental for implementing lock-free algorithms, allowing threads to attempt to update a value only if it hasn't been changed by another thread since it was last read.

It is important to note that Interlocked operations are only atomic for the specific operations they provide. If you need to perform a sequence of operations that must be treated as a single, indivisible unit, you will still need to use traditional locking mechanisms.

Methods

Name Description
CompareExchange Atomically compares two variables for equality and, if they are equal, replaces the second variable with a third variable and returns the original value of the second variable.
Decrement Atomically decrements the specified variable by 1.
Exchange Atomically sets the specified variable to the specified new value and returns the original value of the variable.
Increment Atomically increments the specified variable by 1.
Add Atomically adds two 32-bit integers and returns the sum.
Read Atomically reads the value of the specified variable.

Method CompareExchange<T>(ref T location1, T value, T comparand)

Atomically compares two variables for equality and, if they are equal, replaces the second variable with a third variable and returns the original value of the second variable.

Summary

This is the cornerstone of many lock-free algorithms. It checks if the current value of location1 is equal to comparand. If it is, the value of value is placed into location1. The original value of location1 is returned, regardless of whether the exchange occurred.

Remarks

Use this method when you need to update a shared variable only if its current value matches an expected value. This prevents race conditions where another thread might modify the variable between your read and write operations.

Parameters

  • ref T location1: A reference to the variable with which the comparison is made and, if equal, replaced.
  • T value: The new value to set the variable to if the comparison is equal.
  • T comparand: The variable with which to compare the contents of location1.

Returns

The original value of location1.

Example

// Example using CompareExchange to update a reference atomically
object lockToken = new object();
object oldValue = null;
object newValue = new object();

// Attempt to swap 'oldValue' with 'newValue' if 'oldValue' is null
oldValue = Interlocked.CompareExchange(ref lockToken, newValue, null);

if (oldValue == null)
{
    Console.WriteLine("Successfully acquired the lock.");
}
else
{
    Console.WriteLine("Lock was already held.");
}

Method Decrement(ref int location)

Atomically decrements the specified variable by 1.

Summary

Decrements the integer variable referred to by location by one. This operation is guaranteed to be thread-safe.

Parameters

  • ref int location: A reference to the 32-bit integer to be decremented.

Returns

The decremented value.

Example

// Example using Interlocked.Decrement
int counter = 10;
int newValue = Interlocked.Decrement(ref counter);
Console.WriteLine($"Counter is now: {counter}, returned value: {newValue}"); // Output: Counter is now: 9, returned value: 9

Method Exchange<T>(ref T location1, T value)

Atomically sets the specified variable to the specified new value and returns the original value of the variable.

Summary

Replaces the value at location1 with value and returns the value that was at location1 before the replacement. This operation is atomic.

Parameters

  • ref T location1: A reference to the variable to be replaced.
  • T value: The new value to set the variable to.

Returns

The original value of location1.

Example

// Example using Interlocked.Exchange
string oldName = "Alice";
string newName = "Bob";

string originalName = Interlocked.Exchange(ref oldName, newName);

Console.WriteLine($"Original name: {originalName}"); // Output: Original name: Alice
Console.WriteLine($"Current name: {oldName}");      // Output: Current name: Bob

Method Increment(ref int location)

Atomically increments the specified variable by 1.

Summary

Increments the integer variable referred to by location by one. This operation is guaranteed to be thread-safe.

Parameters

  • ref int location: A reference to the 32-bit integer to be incremented.

Returns

The incremented value.

Example

// Example using Interlocked.Increment
int counter = 5;
int newValue = Interlocked.Increment(ref counter);
Console.WriteLine($"Counter is now: {counter}, returned value: {newValue}"); // Output: Counter is now: 6, returned value: 6

Method Add(ref int location, int value)

Atomically adds two 32-bit integers and returns the sum.

Summary

Adds the specified value to the integer variable referred to by location. The result is stored back in location, and the final sum is returned. This operation is atomic.

Parameters

  • ref int location: A reference to the 32-bit integer to which the value will be added.
  • int value: The 32-bit integer to add to location.

Returns

The sum of the original value of location and value.

Example

// Example using Interlocked.Add
int balance = 100;
int deposit = 50;

int newBalance = Interlocked.Add(ref balance, deposit);

Console.WriteLine($"Current balance: {balance}"); // Output: Current balance: 150
Console.WriteLine($"New balance returned: {newBalance}"); // Output: New balance returned: 150

Method Read(ref long location)

Atomically reads the value of the specified 64-bit variable.

Summary

Reads a 64-bit integer in an atomic operation. This is important on architectures where a read of a 64-bit value might not be atomic.

Parameters

  • ref long location: A reference to the 64-bit integer to read.

Returns

The value read from the specified variable.

Example

// Example using Interlocked.Read
long largeCounter = 10000000000L;
long currentValue = Interlocked.Read(ref largeCounter);
Console.WriteLine($"The current value is: {currentValue}"); // Output: The current value is: 10000000000