Is a bool read/write atomic in C#

62,726

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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.

Share:
62,726
dbkk
Author by

dbkk

Updated on July 08, 2022

Comments

  • dbkk
    dbkk almost 2 years

    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) { ... }
    }
    
  • Dimon Buzz
    Dimon Buzz over 15 years
    Wait a second... reads and writes to reference types (e.g. any Object) is atomic?
  • user142350
    user142350 over 14 years
    The pointer itself, reassigning it, is atomic (i.e. Foo foo1 = foo2;
  • Eamon Nerbonne
    Eamon Nerbonne over 14 years
    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.
  • MindStalker
    MindStalker about 14 years
    "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.
  • configurator
    configurator about 13 years
    @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?
  • Eamon Nerbonne
    Eamon Nerbonne about 13 years
    @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.
  • dbkk
    dbkk almost 11 years
    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.
  • stux
    stux almost 11 years
    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
  • TiMoch
    TiMoch over 10 years
    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
    TiMoch over 10 years
    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;
  • v.oddou
    v.oddou almost 9 years
    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".
  • Mike de Klerk
    Mike de Klerk over 8 years
    If writing to int and long are atomic, then when use Interlocked.Add(ref myInt); e.g.?
  • Jannis Froese
    Jannis Froese over 8 years
    @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.
  • johnny 5
    johnny 5 over 6 years
    @Larsenal, you're answer in ambigous, Yes, this is atomic, and Yes I need a lock?
  • xmedeko
    xmedeko over 6 years
    Well, if you have lock(), you do not need volatile.
  • ceztko
    ceztko over 4 years
    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.