C# – Thread-safe bool properties using Locks vs Interlocked

The following bool property is not thread-safe.

public class NuclearPowerPlant { public bool MeltdownIsHappeningRightNow { get; set; } }
Code language: C# (cs)

Why is this thread un-safe?

Let’s say you have two threads running at the same time. One thread is reading the bool property, while the other thread is changing the value from false to true. It’s possible for the reader thread to get the stale value (false instead of true).

You’ll have to decide if this matters in your system or not. It may be OK for your code to read stale values. If it’s critical that you read the correct values each time, then you’ll need to make the property thread-safe.

In this article, I’ll show two ways to make this property thread-safe: by using a lock and by using the Interlocked class.

Blocking approach – use a lock

Locks only allow one thread into the locked section at a time. When other threads hit the lock, they’ll block until the lock is released. This incurs overhead, but it guarantees thread-safety. There’s always a price to pay for thread-safety.

The following code shows how to use a lock to make the bool property thread-safe.

public class NuclearPowerPlant { private object meltdownLock = new object(); private bool _meltdownIsHappening; public bool MeltdownIsHappeningRightNow { get { lock (meltdownLock) { return _meltdownIsHappening; } } set { lock (meltdownLock) { _meltdownIsHappening = value; } } } }
Code language: C# (cs)

Nonblocking approach – use the Interlocked class

Locks are deceivingly complex. They spread complexity all over the code that uses them. The more places that use the locks, the more complex the code gets.

Bad code is complex all over. Good code isolates complexity and shields the rest of the code from it.

The Interlocked class provides a lock-free, non-blocking approach to thread-safety. Not only does it isolate the complexity, but it also provides better performance by eliminating the overhead incurred by locks.

The following code shows how to use the Interlocked class to make the bool property thread-safe.

public class NuclearPowerPlant { private long _meltdownIsHappening = 0; public bool MeltdownIsHappeningRightNow { get { /* Interlocked.Read() is only available for int64, * so we have to represent the bool as a long with 0's and 1's */ return Interlocked.Read(ref _meltdownIsHappening) == 1; } set { Interlocked.Exchange(ref _meltdownIsHappening, Convert.ToInt64(value)); } } }
Code language: C# (cs)

4 thoughts on “C# – Thread-safe bool properties using Locks vs Interlocked”

    • I wouldn’t suggest using the volatile keyword to try to make code thread-safe. Use lock / Interlocked instead.

      Reply
  1. I don’t think usage of Interlocked class saves you from thread unsafety here.

    What if you do Interlocked.Read, but before (== 1) comparison happens, the execution context is changed to a thread running Interlocked.Exchange?
    Then Interlocked.Exchange would change the value, and the comparison would still return the wrong value.

    No?

    Reply
    • Good question. This article isn’t trying to solve the problem you’re referring to though. After Interlocked.Read() returns a value, it’s entirely possible for the value to be out of date. Notice the same thing happens with the lock() approach (i.e. after the value is returned to the client code calling the getter, it could already be out of date). We are always dealing with the last known value at the time of the read. After the read, it could be out of date. To guarantee we are always dealing with the latest version after the read, we’d have to prevent the update from happening while we are using the value from the read. Therefore, we’d need to pull the locking up into the client code. ​

      The problem this article is showing is how to synchronize the threads (using the Interlocked and lock() approaches) so that the reader thread always reads the last known value during the read.

      To summarize:
      1. Without synchronization, the reader thread could read an old value DURING the read (due to an old value being cached and not updated for that thread).
      2. With synchronization shown in this article, the reader thread always reads the last known value DURING the read. The value could get changed AFTER the read.
      3. With the problem you’re referring to, the reader thread would need to read the last known value DURING the read and the value would need to remain the same AFTER the read (while the client code is using it).

      Reply

Leave a Comment