开发者

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 开发者_StackOverflowatomically. 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?


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.


Interlocked.Exchange has a return value, allowing you to know what value you just replaced. It's the combination of setting a new value and obtaining the old value that these methods achieve.


Exchanging a memory value and the contents of a CPU register is in general not atomic. You both need to read and write the memory location. Furthermore, the Interlocked methods guarantees that the operation is atomic even on multi-core computers where each core has its own cache and potentially its own view of the main memory.


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. Those two together are not atomic.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜