Are volatile variables useful? If yes then when?
c#, multithreading, volatile
Solution
This question is very confusing. Let me try to break it down.
Are volatile variables useful?
Yes. The C# team would not have added a useless feature.
If yes then when?
Volatile variables are useful in certain highly performance-sensitive multithreaded applications where the application architecture is predicated on sharing memory across threads.
As an editorial aside, I note that it should be rare for normal line-of-business C# programmers to be in any of these situations. First, the performance characteristics we are talking about here are on the order of tens of nanoseconds; most LOB applications have performance requirements measured in seconds or minutes, not in nanoseconds. Second, most LOB C# applications can do their work with only a small number of threads. Third, shared memory is a bad idea and a cause of many bugs; LOB applications which use worker threads should not use threads directly, but rather use the Task Parallel Library to safely instruct worker threads to perform calculations, and then return the results. Consider using the new `await` keyword in C# 5.0 to facilitate task-based asynchrony, rather than using threads directly.
Any use of volatile in a LOB application is a big red flag and should be heavily reviewed by experts, and ideally eliminated in favour of a higher-level, less dangerous practice.
lock will prevent instruction reordering.
A lock is described by the C# specification as being a special point in the code such that certain special side effects are guaranteed to be ordered in a particular way with respect to entering and leaving the lock.
volatile because will force CPU to always read value from memory (then different CPUs/cores won't cache it and they won't see old values).
What you are describing is implementation details for how volatile could be implemented; there is not a requirement that volatile be implemented by abandoning caches and going back to main memory. The requirements of volatile are spelled out in the specification.
Interlocked operations perform change + assignment in a single atomic (fast) operation.
It is not clear to me why you have parenthesized "fast" after "atomic"; "fast" is not a synonym for "atomic".
How lock will prevent cache problem?
Again: lock is documented as being a special event in the code; a compiler is required to ensure that other special events have a particular order with respect to the lock. How the compiler chooses to implement those semantics is an implementation detail.
Is it implicit a memory barrier in a critical section?
In practice yes, a lock introduces a full fence.
Volatile variables can't be local
Correct. If you are accessing a local from two threads then the local must be a special local: it could be a closed-over outer variable of a delegate, or in an async block, or in an iterator block. In all cases the local is actually realized as a field. If you want such a thing to be volatile then do not use high-level features like anonymous methods, async blocks or iterator blocks! That is mixing the highest level and the lowest level of C# coding and that is a very strange thing to do. Write your own closure class and make the fields volatile as you see fit.
I read something from Eric Lippert about this but I can't find that post now and I don't remember his answer.
Well I don't remember it either, so I typed "Eric Lippert Why can't a local variable be volatile" into a search engine. That took me to this question:
why can't a local variable be volatile in C#?
Perhaps that is what you're thinking of.
This makes me think they're not implemented with an Interlocked.CompareExchange() and friends.
C# implements volatile fields as volatile fields. Volatile fields are a fundamental concept in the CLR; how the CLR implements them is an implementation detail of the CLR.
in what they're different?
I don't understand the question.
What volatile modifier will do for example in this code?
++_volatileField;
It does nothing helpful, so don't do that. Volatility and atomicity are completely different things. Doing a normal increment on a volatile field does not make the increment into an atomic increment.
Moreover what compiler (beside warnings) will do here:
The C# compiler really ought to suppress that warning if the method being called introduces a fence, as this one does. I never managed to get that into the compiler. Hopefully the team will someday.
The volatile field will be updated in an atomic manner. A fence will be introduced by the increment, so the fact that the volatile half-fences are skipped is mitigated.
How is it possible for non volatile fields?
That's an implementation detail of the CLR.
Does they imply barriers too?
Yes, the interlocked operations introduce barriers. Again, this is an implementation detail.
Doesn't this hurt performance a lot (compared to volatile)?
First off, comparing the performance of broken code to working code is a waste of time.
Second, if you do feel like wasting time, you are perfectly capable of measuring the performance of each yourself. Write the code both ways, get out a stopwatch, run it a trillion times each way, and you'll know which is faster.
If volatile doesn't imply barriers but others do then why we can't use them as on local variables?
I can't even begin to make sense of this question.
Problem
Answering this question made me think about something is still not clear for me. Let's first assume we read all from this post and this post. [begin edit] Maybe it's not so obvious (Italian humor?!) but title is just pretty provocative: of course there should be a reason if `volatile` has been included in C#, I just can't understand exact one.[end edit] In short we know we have three tools to share a variable between threads: - `lock` because this will prevent instruction reordering. - `volatile` because will force CPU to always read value from memory (then different CPUs/cores won't cache it and they won't see old values). - Interlocked operations (`Increment`/`Decrement` and `CompareExchange`) because they'll perform change + assignment in a single atomic (fast faster than, for example, a volatile + lock) operation. What I don't understand (C# specs reference would be appreciated): - How lock will prevent cache problem? Is it implicit a memory barrier in a critical section? - Volatile variables can't be local (I read something from Eric Lippert about this but I can't find that post now and I don't remember his comments and - to be honest - I didn't even understand it very well). This makes me think they're not implemented with an `Interlocked.CompareExchange()` and friends, in what they're different? What `volatile` modifier will do for example in this code? ``` volatile int _volatileField = 0; int _normalField = 0; void test() { Interlocked.Increment(ref _normalField); ++_volatileField; } ``` [begin edit] previous example involves atomic read + write, let's change it to `_volatileField = 1;`, here I'm not talking about atomic operations. [end edit] Moreover what compiler (beside warnings) will do here: ``` Interlocked.Increment(ref _volatileField); ``` They seems pretty different things (as I would imagine they are) but for my understanding `Interlocked.Increment()` operand should implicitly be volatile (then it'll add just atomic increment). How is it possible for non volatile fields? Does they imply barriers too? Doesn't this hurt performance a lot (compared to volatile)? If `volatile` doesn't imply barriers but others do then why we can't use them as on local variables? Especially when used in, for example, parallel loops this will hurt performance in a significant way (I'm thinking about small functions with little code that work on a lot of data where data cache may be well used). [begin edit] I found previous sentence was really unclear (sorry for my English). What I mean is: if performance (of `volatile` compared to `CompareExchange`, where comparison is applicable) are better (yes we can measure and in some scenario difference is measurable and visible) then why we can't use them for local variables? I'm thinking about parallel loops that manipulates a lot of data (where both overhead and barriers may hurt performance a lot).[end edit]