volatile as a synchronization mechanism
Let's say i have a class Foo and it has a static member variable named Count ( type is integer). This variable was used in a multit开发者_开发问答hreaded application and i was using lock synchronization mechanism before doing any read/write to this variable. As I am reading this article Volatile I am getting the impression that i can remove all those locks around this variable and just use volatile keyword at declaration of this variable. That should take care of all the synchnronization related stuff. Is this correct? What are the pros and cons to this approach?
I can remove all those locks around this variable and just use volatile keyword at declaration of this variable. That should take care of all the synchnronization related stuff. Is this correct?
Maybe. Maybe not. Getting multithreaded code correct is extremely difficult. Getting low-lock multithreaded code correct is best left to experts.
What are the pros and cons to this approach?
The pros are that it can be several nanoseconds faster to avoid the lock. The cons are, if you get low-lock programming wrong then your program looks like it works just fine, and then has bizarre failure modes that are impossible to debug or reproduce.
You should only go with a low-lock solution when your performance analysis has led you to conclude that a low-lock solution is the only way to attain the performance goal your customers require you to attain. And you should only go with a low-lock solution when you have a thorough and deep understanding of every optimization that every possible CPU your program will ever run on can perform on low-lock code. You'll need to deeply understand the memory model that is guaranteed to you by the CLR, what is guaranteed by hardware, and what all the differences are.
I don't possess this understanding myself. That's why I don't write anything except the most trivial low-lock code, and I have what little low-lock code I do write carefully reviewed by industry-leading experts.
If all you're doing is reading that variable from multiple threads and writing to it from one thread, then volatile
will probably work. But if you're updating the value (i.e. incrementing it) on multiple threads, then you need some kind of synchronization.
For example, if you're writing this:
Count = Count + 1;
Remember, an increment requires three operations: read, increment, write. On multiple threads, you can have a problem. Imagine that the initial value of Count
is zero.
- Thread 1 reads Count (value 0)
- Thread 2 reads Count (value 0)
- Thread 1 increments its value (1)
- Thread 1 writes Count (value 1)
- Thread 2 increments its value (1)
- Thread 2 writes Count (value 1)
The final value of Count
is 1, when it should be 2.
You want to look into the Interlocked class. In particular, Interlocked.Increment
and Interlocked.CompareExchange
.
The volatile
keyword is completely different than a lock
. A lock
implies that there are multiple statements that need to take place as a single unit of work with consistent state, such as if you want to ensure that no other threads can add or remove items from a collection while you're accessing a specific item out of the collection or counting the items in the collection. The volatile
keyword affects how the compiler behaves as it tries to optimize your code when it generates the machine instructions from your source code. It's possible that the compiler may re-order statements in a specific block of code in a way that doesn't affect that code block, but that reordering may not be valid if your variable's value can be changed from another thread. Using volatile
tells the compiler not to consider such optimizations, and to always read your variable's value from memory, not out of a cached register.
Jon Skeet discusses this in greater detail in his article Volatility, Atomicity and Interlocking.
精彩评论