Do I Need to Lock or Mark as Volatile When Accessing a Simple Boolean Flag in C#

Do I need to lock or mark as volatile when accessing a simple boolean flag in C#?

Firstly, threading is tricky ;-p

Yes, despite all the rumours to the contrary, it is required to either use lock or volatile (but not both) when accessing a bool from multiple threads.

For simple types and access such as an exit flag (bool), then volatile is sufficient - this ensures that threads don't cache the value in their registers (meaning: one of the threads never sees updates).

For larger values (where atomicity is an issue), or where you want to synchronize a sequence of operations (a typical example being "if not exists and add" dictionary access), a lock is more versatile. This acts as a memory-barrier, so still gives you the thread safety, but provides other features such as pulse/wait. Note that you shouldn't use a lock on a value-type or a string; nor Type or this; the best option is to have your own locking object as a field (readonly object syncLock = new object();) and lock on this.

For an example of how badly it breaks (i.e. looping forever) if you don't synchronize - see here.

To span multiple programs, an OS primitive like a Mutex or *ResetEvent may also be useful, but this is overkill for a single exe.

Do i need to lock a bool when i'm reading/writing it in two consecutive statements?

To ensure the thread safety, you have to make the comparison and the assignment to a single atomic operation. Otherwise there is always a chance that another thread can pass the comparison while the assignment is being performed by the first thread. The volatile keyword doesn't help here, because it rather tells the compiler (and the runtime) that no optimization is allowed which could change the sequence of the read-write operations this variable is involved into.

(More about the volatile thing you can read in this great article by Eric Lippert.)

The simple (slightly slower one) solution would be to set up a critical section around the comparison and the assignment:

lock (_someLockObject)
{
if (_isSynchronizing)
{
return;
}

_isSynchronizing = true;
}

There is a faster solution however, but not for a bool variable. For an int, you could use the Interlocked.CompareExchange() method.

Let's pretend that int _isSynchronizing = 0 means false and _isSynchronizing = 1 means true.

Then you can use this statement:

if (Interlocked.CompareExchange(ref _isSynchronizing, 1, 0) == 1)
{
// If the original val == 1, we're already synchronizing
return;
}

This is slightly faster than using a Monitor, but there is no bool overload.

When should the volatile keyword be used in C#?

I don't think there's a better person to answer this than Eric Lippert (emphasis in the original):

In C#, "volatile" means not only "make sure that the compiler and the
jitter do not perform any code reordering or register caching
optimizations on this variable". It also means "tell the processors to
do whatever it is they need to do to ensure that I am reading the
latest value, even if that means halting other processors and making
them synchronize main memory with their caches".

Actually, that last bit is a lie. The true semantics of volatile reads
and writes are considerably more complex than I've outlined here; in
fact they do not actually guarantee that every processor stops what it
is doing
and updates caches to/from main memory. Rather, they provide
weaker guarantees about how memory accesses before and after reads and
writes may be observed to be ordered with respect to each other
.
Certain operations such as creating a new thread, entering a lock, or
using one of the Interlocked family of methods introduce stronger
guarantees about observation of ordering. If you want more details,
read sections 3.10 and 10.5.3 of the C# 4.0 specification.

Frankly, I discourage you from ever making a volatile field. Volatile
fields are a sign that you are doing something downright crazy: you're
attempting to read and write the same value on two different threads
without putting a lock in place. Locks guarantee that memory read or
modified inside the lock is observed to be consistent, locks guarantee
that only one thread accesses a given chunk of memory at a time, and so
on. The number of situations in which a lock is too slow is very
small, and the probability that you are going to get the code wrong
because you don't understand the exact memory model is very large. I
don't attempt to write any low-lock code except for the most trivial
usages of Interlocked operations. I leave the usage of "volatile" to
real experts.

For further reading see:

  • Understand the Impact of Low-Lock Techniques in Multithreaded Apps
  • Sayonara volatile

Is lock or volatile required when worker threads write non-competitively to local or class variables?

Writes to reference types (i.e. Object) and word-sized value types (i.e. int in a 32 bit system) are atomic. This means that when you peek at the values (position 6) you can be sure that you either get the old value or the new value, but not something else (if you had a type such as a large struct it could be spliced, and you could read the value when it was half way through being written). You don't need a lock or volatile, so long as you're willing to accept the potential risk of reading stale values.

Note that because there is no memory barrier introduced at this point (a lock or use of volatile both add one) it's possible that the variable has been updated in the other thread, but the current thread isn't observing that change; it can be reading a "stale" value for (potentially) quite some time after it has been changed in the other thread. The use of volatile will ensure that the current thread can observe changes to the variable sooner.

You can be sure that you'll have the appropriate value after the call to WaitAll, even without a lock or volatile.

Also note that while you can be sure the reference to the reference type is written atomically, your program makes no guarantee about the observed order of any changes to the actual object that the reference refers to. Even if, from the point of view of the background thread, the object is initialized before it is assigned to the instance field, it may not happen in that order. The other thread can therefore observe the write of the reference tot he object but then follow that reference and find an object in an initialize, or partially initialized, state. Introducing a memory barrier (i.e. through the use of a volatile variable can potentially allow you to prevent the runtime from making such re-orderings, thus ensuring that doesn't happen. This is why it's better to just not do this in the first place and to just have the two tasks return the results that they generate rather than manipulating a closed over variable.

WaitAll will introduce a memory barrier, in addition to ensuring that the two tasks are actually finished, which means that you know that the variables are up-to-date and will not have the old stale values.

Does a reference field need to be volatile if I modify it during a lock?

EDIT: Your solution will work. Lock creates a full fence so any caching is prevented, basically meaning you'll always get the most recent value for the list reference. The only thing, as suggested in the comments is the fact that you should do the locking on a neutral object, not the list itself.

The following is wrong!! But I let it here anyway to show how fu*** hard threading might be... the fact is the following reasoning is defeated by the fact that lock creates a full fence.

Yeah, it can happen so don't do it
that way.

It won't get better even if you did
the lock into a readonly whatever
object.

See what might happen (although most
of the time it WON'T happen).

ThreadA and ThreadB are executing on
different processors, each one with
its own cache memory which holds the
reference to incovationQueue.

  • ThreadB locks invocationQueue, the lock is done to a reference which is
    taken for the cache of processor1, not
    to a variable name.
  • ThreadB copies the invocationQueue.
  • ThreadA locks invocationQueue, the lock is done to a reference which is
    taken for the cache on processor2 and
    which, in this moment is the same as
    the one in processor1, and starts
    waiting.
  • ThreadB creates a new List and assigns it to the invocationQueue, the
    cache in the processor1 is updated but
    since the variable is NOT volatile
    that's all that happens.
  • ThreadA enters the lock and gets the reference from his cache, which points
    to the old reference, therefore you
    end up adding the variable to the old
    list.

So you need to make the list volatile
AND use the lock if you're going to be
playing with the reference itself.

C# and thread-safety of a bool

No, not all of them are thread safe.

Case one isn't actually completely thread safe, or better saying - it isn't thread safe at all. Even if operations with boolean are atomic, variable value can be stored in a cache, and so, as in multicore CPU each core has it's own cache, value can be potentially corrupted.

Going even further, compiler and CPU can perform some internal optimizations, including instruction reordering, which can harmfully affect your program's logic.

You can add the volatile keyword, to notify the compiler that this field is used in a multi-threaded context. It will fix problems with cache and instruction reordering, but doesn't give you truly "thread safe" code (as write operations still will be not synchronized). Also volatile cannot be applied to local variable.

So when dealing with multi-threading you always have to use some technique of thread synchronization on valuable resources.

For more information - read this answer, which has some deeper explanation of different techniques. (example there is about int, but is doesn't really matter, it describes general approach.)



Related Topics



Leave a reply



Submit