views:

338

answers:

4

Suppose I have a variable "counter", and there are several threads accessing and setting the value of "counter" by using Interlocked, i.e.:

int value = Interlocked.Increment(ref counter);

and

int value = Interlocked.Decrement(ref counter);

Can I assume that, the change made by Interlocked will be visible in all threads?

If not, what should I do to make all threads synchronize the variable?

EDIT: someone suggested me to use volatile. But when I set the "counter" as volatile, there is compiler warning "reference to volatile field will not be treated as volatile".

When I read online help, it said, "A volatile field should not normally be passed using a ref or out parameter".

A: 

Interlocked ensures that only 1 thread at a time can update the value. To ensure that other threads can read the correct value (and not a cached value) mark it as volatile.

public volatile int Counter;

Peter Morris
when I marked as volatile, there is complier warning. "reference to volatile field will not be treat as volatile".
chaowman
ignore that warning for this case: http://stackoverflow.com/questions/425132/a-reference-to-a-volatile-field-will-not-be-treated-as-volatile-implications
Lasse V. Karlsen
Apparently you don't need Volatile if you are using Interlocked, but if you are modifying without using Interlocked then you do.
Peter Morris
Just to clarify. Mark items as volatile if you are going to read them without obtaining a lock. Use Interlocked.Increment to synchronise updating, or use a lock() on something. The warning you get about "ref not being treated as volatile" is generic and can be ignored in the case of Interlocked.
Peter Morris
I'm afraid that this is not correct answer. Any other threads can see interlocked operation. It has visibility to all threads. Volatile is not necessary. If I'm wrong, please correct me.
minjang
A: 

Actually, they aren't. If you want to safely modify counter, then you are doing the correct thing. But if you want to read counter directly you need to declare it as volatile. Otherwise, the compiler has no reason to believe that counter will change because the Interlocked operations are in code that it might not see.

MSN
Why the downvote? This is correct.
MSN
+1  A: 

Can I assume that, the change made by Interlocked will be visible in all threads?

This depends on how you read the value. If you "just" read it, then no, this won't always be visible in other threads unless you mark it as volatile. That causes an annoying warning though.

As an alternative (and much preferred IMO), read it using another Interlocked instruction. This will always see the updated value on all threads:

int readvalue = Interlocked.CompareExchange(ref counter, 0, 0);

which returns the value read, and if it was 0 swaps it with 0.

Motivation: the warning hints that something isn't right; combining the two techniques (volatile & interlocked) wasn't the intended way to do this.

romkyns
I don't know why, but I never thought to do this. I was always wondering where there was only an Interlocked.Read for longs and not for ints. This will do the same thing.
Michael Covelli
+1  A: 

InterlockedIncrement/Decrement on x86 CPUs (x86's lock add/dec) are automatically creating memory barrier which gives visibility to all threads (i.e., all threads can see its update as in-order, like sequential memory consistency). Memory barrier makes all pending memory loads/stores to be completed. volatile is not related to this question although C# and Java (and some C/C++ compilers) enforce volatile to make memory barrier. But, interlocked operation already has memory barrier by CPU.

Please also take a look my another answer in stackoverflow.

Note that I have assume that C#'s InterlockedIncrement/Decrement are intrinsic mapping to x86's lock add/dec.

minjang