Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using of Interlocked.Exchange for updating of references and Int32

It is known that a reference takes 4 Bytes of memory in 32 bit processor and 8 Bytes - in 64 bit processor. So, processors guarantee that single reads from and writes to memory in increments of the natural word size of the machine will be carried out atomically. On the other hand there are 2 methods in Interlocked class:

public static int Exchange(     ref int location1,     int value ) 

and

public static T Exchange<T>(     ref T location1,     T value ) where T : class 

So, the question is why Interlocked.Exchange is needed for Int32 and for reference types? Couldn't it be done safely by just using of simple assignment because it is atomic?

like image 734
Oleg Dudnyk Avatar asked Aug 03 '11 14:08

Oleg Dudnyk


People also ask

Why use Interlocked Exchange?

Interlock. Exchange returns the original value while performing an atomic operation. The whole point is to provide a locking mechanism. So it is actually two operations: read original value and set new value.

When should you use the interlocked class?

The methods of this class help protect against errors that can occur when the scheduler switches contexts while a thread is updating a variable that can be accessed by other threads, or when two threads are executing concurrently on separate processors.

Is interlocked exchange thread safe?

The Interlocked class provides a number of static methods that perform atomic operations. These can generally be regarded as thread-safe.

Is reference assignment atomic in C#?

Reference assignment is guaranteed to be atomic on all . NET platforms.


1 Answers

It is not only about atomicity. It is also about memory visibility. Variable can be stored in main memory or in CPU cache. If the variable is only stored in CPU cache it will not be visible to threads running on different CPU. Consider following example:

public class Test {     private Int32 i = 5;      public void ChangeUsingAssignment() {         i = 10;     }      public void ChangeUsingInterlocked() {         Interlocked.Exchange(ref i, 10);     }      public Int32 Read() {         return Interlocked.CompareExchange(ref i, 0, 0);     } } 

Now if you call 'ChangeUsingAssignment' on one thread and 'Read' on another thread the return value may be 5, not 10. But if you call ChangeUsingInterlocked, 'Read' will return 10 as expected.

 ----------         ------------         ------------------- |   CPU 1  |  -->  |   CACHE 1  |  -->  |                   |  ----------         ------------        |                   |                                         |        RAM        |  ----------         ------------        |                   | |   CPU 2  |  -->  |   CACHE 2  |  -->  |                   |  ----------         ------------         ------------------- 

In the diagram above 'ChangeUsingAssignement' method may result in value 10 get 'stuck' in CACHE 2 and not make it to RAM. When CPU 1 later tries to read it, it will get the value from RAM where it is still 5. Using Interlocked instead of ordinary write will make sure that value 10 gets all the way to the RAM.

like image 153
Dmitry Avatar answered Oct 13 '22 03:10

Dmitry