Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

We need to lock a .NET Int32 when reading it in a multithreaded code?

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:

  1. Adding to "int example" the "volatile" property
  2. Inserting a Thread.MemoryBarrier(); before the actual reading of "int example"
  3. Read "int example" inside a "lock(thisLock)"

And I also think that "volatile" is the most elegant solution.

like image 919
Jader Dias Avatar asked Dec 27 '08 18:12

Jader Dias


People also ask

Do you need to lock for read C#?

Reading does not require a lock; as long as you don't care about the 'correctness' of the read. It is only dangerous if you attempt to write without a lock.

What is lock in multithreading C#?

C# Lock keyword ensures that one thread is executing a piece of code at one time. The lock keyword ensures that one thread does not enter a critical section of code while another thread is in that critical section. Lock is a keyword shortcut for acquiring a lock for the piece of code for only one thread.

When should I use lock C#?

C# lock in thread The lock keyword is used to get a lock for a single thread. A lock prevents several threads from accessing a resource simultaneously. Typically, you want threads to run concurrently. Using the lock in C#, we can prevent one thread from changing our code while another does so.

Does lock block thread C#?

Locks will block other threads from executing the code contained in the lock block. The threads will have to wait until the thread inside the lock block has completed and the lock is released.


2 Answers

Locking accomplishes two things:

  • It acts as a mutex, so you can make sure only one thread modifies a set of values at a time.
  • It provides memory barriers (acquire/release semantics) which ensures that memory writes made by one thread are visible in another.

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 Add repeatedly and another thread calling Read. Atomicity on its own would ensure that you only ended up reading a multiple of 8 - and if there were two threads calling Add your lock would ensure that you didn't "lose" any additions. However, it's quite possible that your Read thread would only ever read 0, even after Add had 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.

like image 140
Jon Skeet Avatar answered Oct 16 '22 08:10

Jon Skeet


It all depends on the context. When dealing with integral types or references you might want to use members of the System.Threading.Interlocked class.

A typical usage like:

if( x == null )
  x = new X();

Can be replaced with a call to Interlocked.CompareExchange():

Interlocked.CompareExchange( ref x, new X(), null);

Interlocked.CompareExchange() guarantees that the comparison and exchange happen as an atomic operation.

Other members of the Interlocked class, such as Add(), Decrement(), Exchange(), Increment() and Read() all perform their respective operations atomically. Read the documentation on MSDN.

like image 26
Eddie Velasquez Avatar answered Oct 16 '22 09:10

Eddie Velasquez