I was reading the following article: http://msdn.microsoft.com/en-us/magazine/cc817398.aspx ‘Solving 11 Likely Problems In Your Multithreaded Code’ by Joe Duffy
And it raised me a question: ‘We need to lock a .NET Int32 when reading it in a multithreaded code?’
I understand that if it was an Int64 in a 32-bit SO it could tear, as it is explained in the article. But for Int32 I imagined the following situation:
class Test { private int example = 0; private Object thisLock = new Object(); public void Add(int another) { lock(thisLock) { example += another; } } public int Read() { return example; } }
I don’t see a reason to include a lock in the Read method. Do you?
Update Based on the answers (by Jon Skeet and ctacke) I understand that the code above still vulnerable to multiprocessor caching (each processor has its own cache, unsynchronized with others). All the three modifications bellow fix the problem:
- Adding to ‘int example’ the ‘volatile’ property
- Inserting a Thread.MemoryBarrier(); before the actual reading of ‘int example’
- Read ‘int example’ inside a ‘lock(thisLock)’
And I also think that ‘volatile’ is the most elegant solution.
Locking accomplishes two things:
Most people understand the first point, but not the second. Suppose you used the code in the question from two different threads, with one thread calling
Addrepeatedly and another thread callingRead. Atomicity on its own would ensure that you only ended up reading a multiple of 8 – and if there were two threads callingAddyour lock would ensure that you didn’t ‘lose’ any additions. However, it’s quite possible that yourReadthread would only ever read 0, even afterAddhad been called several times. Without any memory barriers, the JIT could just cache the value in a register and assume it hadn’t changed between reads. The point of a memory barrier is to either make sure something is really written to main memory, or really read from main memory.Memory models can get pretty hairy, but if you follow the simple rule of taking out a lock every time you want to access shared data (for read or write) you’ll be okay. See the volatility/atomicity part of my threading tutorial for more details.