96

Is accessing a bool field atomic in C#? In particular, do I need to put a lock around:

class Foo
{
   private bool _bar;

   //... in some function on any thread (or many threads)
   _bar = true;

   //... same for a read
   if (_bar) { ... }
}
dbkk
  • 12,643
  • 13
  • 53
  • 60
  • 1
    [This earlier question seems related and may have additional useful answers.](http://stackoverflow.com/questions/9666/is-accessing-a-variable-in-c-an-atomic-operation) – robaker Sep 12 '08 at 16:37
  • 1
    Yes, but (possibly) also yes. Yes accessing/setting a bool field is atomic, BUT the if operation is not (refer to Dror Helper's answer below) so you may still need a lock as well. – JPProgrammer Sep 29 '15 at 03:27

4 Answers4

137

Yes.

Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.

as found in C# Language Spec.

Edit: It's probably also worthwhile understanding the volatile keyword.

antiduh
  • 11,853
  • 4
  • 43
  • 66
Larsenal
  • 49,878
  • 43
  • 152
  • 220
  • Wait a second... reads and writes to reference types (e.g. any Object) is atomic? – core Sep 19 '08 at 23:11
  • 10
    The pointer itself, reassigning it, is atomic (i.e. Foo foo1 = foo2; – user142350 Aug 24 '09 at 15:08
  • 1
    In my experience volatile is generally not a great idea: if you need volatile, you generally care about "simultaneous" actions - and even with a volatile, those are quite easy to get wrong. For instance the above if statement would require quite a bit of extra plumbing if it's to avoid many threadings entering - and if the aim is only to avoid entering until some thread has passed `_bar = true;` at least once, not using volatile at worst means that the if statement won't be take temporarily even though _bar is true. I'd suggest to just use locks if precision is required. – Eamon Nerbonne Sep 15 '09 at 13:46
  • @Eamon: If the case is that the `if` shouldn't be entered until a thread has set `_bar = true;`, then volatile is exactly what you want - why would you need locks here? – configurator Mar 30 '11 at 13:05
  • 4
    @configurator: The question is what exactly do you want. It's easy to get lock-free programs wrong; so unless you really need it, it's better to use a simpler framework (e.g. the TPL). 'volatile' is in other words not wrong, but a sign of tricky (i.e. preferably avoided) code. The OP hasn't really said what he wants, I'm just hesitant to *recommend* volatile willy-nilly. – Eamon Nerbonne Apr 12 '11 at 07:54
  • 13
    Waw. This is a dangerous wording, for C++ people atomic means that any read write are also surrounded by corresponding memory fence. Which is surely not the case in C#. Because otherwise the performance would be horrible since its mandatory for all variables < long. Atomic here in C# parlance, seems to mean than **when the reads or writes eventually happen, they are guaranteed to never be in a broken state**. But it says nothing as to when is "eventually". – v.oddou Jun 25 '15 at 08:35
  • 4
    If writing to int and long are atomic, then when use `Interlocked.Add(ref myInt);` e.g.? – Mike de Klerk Dec 08 '15 at 19:04
  • 10
    @MikedeKlerk The read and write are atomic, but seperate. `i++` is equal to `i=i+1`, meaning you do an atomic read, then addition, then atomic write. Another thread could modify `i` after the read but before the write. For example two threads doing `i++` concurrently on the same i can happen to read at the same time (and thus read the same value), add one to it and then both write the same value, effectively adding only once. Interlocked.Add prevents this. As a general rule the fact that a type is atomic is only useful if there's only one thread writing but many threads reading. – Jannis Froese Jan 20 '16 at 22:59
  • 1
    @Larsenal, you're answer in ambigous, Yes, this is atomic, and Yes I need a lock? – johnny 5 Jan 24 '18 at 15:32
  • Agree with @johnny5, the answer is ambiguous. Also suggesting to understand `volatile` keyword is wrong with regarding to synchronization problems as `volatile` alone does not put memory barrier. – ceztko Oct 15 '19 at 11:05
52

As stated above, bool is atomic, but you still need to remember that it also depends on what you want to do with it.

if(b == false)
{
    //do something
}

is not an atomic operation, meaning that the value of b could change before the current thread executes the code after the if statement.

Pang
  • 9,564
  • 146
  • 81
  • 122
Dror Helper
  • 30,292
  • 15
  • 80
  • 129
32

bool accesses are indeed atomic, but that isn't the whole story.

You don't have to worry about reading a value that is 'incompletely written' - it isn't clear what that could possibly mean for a bool in any case - but you do have to worry about processor caches, at least if details of timing are an issue. If thread #1 running on core A has your _bar in cache, and _bar gets updated by thread #2 running on another core, thread #1 will not see the change immediately unless you add locking, declare _bar as volatile, or explicitly insert calls to Thread.MemoryBarrier() to invalidate the cached value.

McKenzieG1
  • 13,960
  • 7
  • 36
  • 42
  • 1
    "it isn't clear what that could possibly mean for a bool in any case " Items that exist in only one byte of memory at atomic because the entire byte is written to at the same time. Versus items like double which exist in multiple bytes, one byte could be written before the other byte and you could observe a half written memory location. – MindStalker Apr 01 '10 at 19:53
  • 3
    MemoryBarrier() does not invalidate any processor cache. In some architecture, the processor is allowed to reorder reads and writes to main memory for performance. Reordering may happen so long as from the point of view of a single thread, the semantics stay the same. MemoryBarrier() requests the processor to limit the reordering so that memory operations emitted before the barrier are not reordered in such a way that they end up after the barrier. – TiMoch Dec 03 '13 at 21:05
  • 1
    A memory barrier is useful if you create a fat object and switch a reference to it that may be read from other threads. The barrier guarantees the reference is not updated in main memory before the rest of the fat object. Other threads are guaranteed to never see the reference update before the fat object is actually available in main memory. `var fatObject = new FatObject(); Thread.MemoryBarrier(); _sharedRefToFat = fatObject;` – TiMoch Dec 03 '13 at 21:06
2

the approach I have used, and I think is correct, is

volatile bool b = false;

.. rarely signal an update with a large state change...

lock b_lock
{
  b = true;
  //other;
}

... another thread ...

if(b)
{
    lock b_lock
    {
       if(b)
       {
           //other stuff
           b = false;
       }
     }
}

the goal was basically to avoid having to repetively lock an object on every iteration just to check if we needed to lock it in order to provide a large amount of state change information which occurs rarely. I think this approach works. And if absolute consistancy is required, I think volatile would be appropriate on the b bool.

stux
  • 2,526
  • 1
  • 13
  • 6
  • 4
    This is indeed a correct approach to locking in general, but if bools are atomic, then it's simpler (and faster) to omit the lock. – dbkk May 21 '13 at 09:48
  • 3
    Without the lock then the "large state change" will not be done atomically. The lock -> set | check -> lock -> check approach will also ensure that the "//other" code is executed BEFORE the "//other stuff" code. Assuming the "another thread" section is iterating many times (which it is in my case), only having to check a bool, most of the time, but not actually acquire a (possibly contended) lock, is a major performance win – stux Jul 18 '13 at 05:12
  • 3
    Well, if you have `lock()`, you do not need `volatile`. – xmedeko Feb 07 '18 at 13:52