Atomicity & Interlocked
We know that the need for synchronization arises even the simple case of assigning or incrementing a field. Although locking can always satisfy this need, a contended lock means that a thread must block, suffering the overhead and latency of being temporarily descheduled.
The .NET framework's Non-Blocking Synchronization constructs can perform simple operations without ever blocking, pausing, or waiting.
These involve using instructions that are Strictly Atomic, and instructing the compiler to use "volatile" read and write semantics.
A statement is Atomic if it executes as a single indivisible instruction. Strict atomicity means - no possibility of preemption.
In C#, a simple read or assignment on a field of 32 bits or less is atomic (assuming a 32-bit CPU). Operations on larger fields (64 bits) are non-atomic, as are statements that combine more than one read/write operation.
Note - Unary operators of the kind x++ are non-atomic operations – As it requires first reading a variable, then processing it, then writing it back.
| || |
static int x, y;
static long z;
static void Test()
x = 3; // Atomic
z = 3; // Non-atomic (z is 64 bits)
myLocal = z; // Non-atomic (z is 64 bits)
y += x; // Non-atomic (read AND write operation)
x++; // Non-atomic (read AND write operation)
One way to solve to these problems is to wrap the non-atomic operations around a lock statement. Locking, in fact, simulates atomicity.
The Interlocked class, however, provides a simpler and faster solution for simple atomic operations.
Using Interlocked is generally more efficient that obtaining a lock, because it can never block and suffer the overhead of its thread being temporarily descheduled.
Interlocked is also valid across multiple processes – in contrast to the lock statement, which is effective only across threads in the current process.
| || |
static long sum;
Interlocked.Increment(ref sum); // 1
Interlocked.Decrement(ref sum); // 0
Interlocked.Add(ref sum, 3); // 3
Console.WriteLine(Interlocked.Read(ref sum)); // 3
Console.WriteLine(Interlocked.Exchange(ref sum, 10)); // 10
Interlocked.CompareExchange(ref sum, 123, 10); // 123
Hope this helps.
Thanks & Regards,
Arun Manglick || Senior Tech Lead