Is a bool read/write atomic in C#
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.
dbkk
Updated on July 08, 2022Comments
-
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 over 15 yearsWait a second... reads and writes to reference types (e.g. any Object) is atomic?
-
user142350 over 14 yearsThe pointer itself, reassigning it, is atomic (i.e. Foo foo1 = foo2;
-
Eamon Nerbonne over 14 yearsIn 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 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 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 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 almost 11 yearsThis 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 almost 11 yearsWithout 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 over 10 yearsMemoryBarrier() 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 over 10 yearsA 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 almost 9 yearsWaw. 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 over 8 yearsIf writing to int and long are atomic, then when use
Interlocked.Add(ref myInt);
e.g.? -
Jannis Froese over 8 years@MikedeKlerk The read and write are atomic, but seperate.
i++
is equal toi=i+1
, meaning you do an atomic read, then addition, then atomic write. Another thread could modifyi
after the read but before the write. For example two threads doingi++
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 over 6 years@Larsenal, you're answer in ambigous, Yes, this is atomic, and Yes I need a lock?
-
xmedeko over 6 yearsWell, if you have
lock()
, you do not needvolatile
. -
ceztko over 4 yearsAgree with @johnny5, the answer is ambiguous. Also suggesting to understand
volatile
keyword is wrong with regarding to synchronization problems asvolatile
alone does not put memory barrier.