Labels

Monday, June 15, 2009

Threading || Interlocked

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.

 

Reading and writing 64-bit fields is non-atomic on 32-bit CPUs in the sense that two separate 32- bit memory locations are involved. If thread A reads a 64-bit value while thread B is updating it, thread A may end up with a bitwise combination of the old and new values.

 

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.

 

 

class Atomicity

    {

        static int x, y;

        static long z;

        static void Test()

        {

            long myLocal;

            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.

 

 

 

class Program

    {

        static long sum;

        static void Main()

        {

            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

 

 

No comments:

Post a Comment