Is there a systematic way to know whether an operation in C# will be atomic or not? Or are there any general guidelines or rules of thumb?
3 Answers
For something more complete/detailed:
Reads and writes to 32-bit value types are atomic: This includes the following intrinsic value (struct) types: bool, char, byte, sbyte, short, ushort, int, uint, float
. The following types (amongst others) are not guaranteed to be atomic: decimal, double, long, ulong
.
e.g.
int x;
x = 10; // atomic
decimal d;
d = 10m; // not atomic
Reference assignment is also an atomic operation:
private String _text;
public void Method(String text)
{
_text = text; // atomic
}

- 4,976
- 2
- 24
- 71

- 35,463
- 9
- 80
- 98
-
24Not strictly correct- 64 bit values (long) are also atomic if you are on a system whose native word size is 64-bits (e.g. x64 processor running a 64-bit version of windows) – Chris Shain Jul 31 '12 at 17:27
-
1Are references atomic even on a 64-bit application? – Eric Jul 31 '12 at 17:30
-
Nevermind... I think the comment by @ChrisShain answers that question. – Eric Jul 31 '12 at 17:31
-
1@ChrisShain From the C# spec: "Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic. " – Peter Ritchie Jul 31 '12 at 17:38
-
@Eric The C# spec simply says reads and writes from/to "reference types" are atomic. – Peter Ritchie Jul 31 '12 at 17:41
-
1@PeterRitchie the C# spec says that there is no guarantee that longs are atomic, but it does not by any means forbid atomicity. My understanding is that on the 64-bit CLI they are atomic, due to the guarantees in the part of the CLI spec I post in my answer. That said, I am willing to be proven wrong if an authority on the subject can say otherwise why access to a `long` on a 64-bit platform would be non-atomic. – Chris Shain Jul 31 '12 at 17:49
-
@PeterRitchie see the addition to my answer- Eric Lippert has answered this for us. – Chris Shain Jul 31 '12 at 17:52
-
3@ChrisShain you are correct, the spec does not say they are "not" atomic or "never" atomic, I've clarified my answer (which wasn't meant to suggest they were never going to be atomic, just not guaranteed). But, you'd have to compile for x64 to make sure it was. If you didn't compile for x64, the code could run in x86 and thus not be atomic; so, you'd have to assume it wasn't atomic and use lock or Monitor.Enter/Exit to ensure it was accessed atomically. – Peter Ritchie Jul 31 '12 at 17:54
-
Reference reads and reference writes are atomic; a reference assignment combines an (atomic) reference read and an (atomic) reference write, but the operation as a whole is not atomic. – supercat Feb 19 '15 at 18:54
-
@supercat According to the link below, Reference Assignment is atomic. https://stackoverflow.com/questions/2192124/reference-assignment-is-atomic-so-why-is-interlocked-exchangeref-object-object/2193445#2193445 – fdhsdrdark Dec 13 '20 at 08:18
-
@fdhsdrdark: Given `Object x="Foo",y="Bar";`, consider what happens if if thread #1 performs `x=y;` at the same time as another thread performs `y=x;`. C#/.NET semantics (which entail an atomic read followed by an atomic write) would allow for `x` and `y` to hold `Bar`/`Foo`, `Foo`/`Foo`, or `Bar`/`Bar`. If an assignment were atomic as a complete operation, `Bar`/`Foo` would be impossible. – supercat Dec 13 '20 at 09:26
-
@fdhsdrdark: It is so rare for platforms to offer truly atomic assignments which would guarantee that no other operations can occur between the write and the read, that the term "atomic assignment" is often used to describe the non-atomic combination of an atomic read and an atomic write. The combination as a whole, however, is generally not an atomic operation, since the assignments above could be interleaaved as `temp1=y; temp2=x; x=temp1; y=temp2;`. – supercat Dec 13 '20 at 09:32
Yes. Read the CLI specification: http://www.ecma-international.org/publications/standards/Ecma-335.htm. For instance:
I.12.6.6 Atomic reads and writes
A conforming CLI shall guarantee that read and write access to properly aligned memory locations no larger than the native word size (the size of type native int) is atomic (see §I.12.6.2) when all the write accesses to a location are the same size. Atomic writes shall alter no bits other than those written. Unless explicit layout control (see Partition II (Controlling Instance Layout)) is used to alter the default behavior, data elements no larger than the natural word size (the size of a native int) shall be properly aligned. Object references shall be treated as though they are stored in the native word size.
[Note: There is no guarantee about atomic update (read-modify-write) of memory, except for methods provided for that purpose as part of the class library (see Partition IV). An atomic write of a “small data item” (an item no larger than the native word size) is required to do an atomic read/modify/write on hardware that does not support direct writes to small data items. end note]
[Note: There is no guaranteed atomic access to 8-byte data when the size of a native int is 32 bits even though some implementations might perform atomic operations when the data is aligned on an 8-byte boundary. end note]
Regarding the 64-bit long question, Eric Lippert answers it here: https://ericlippert.com/2011/05/31/atomicity-volatility-and-immutability-are-different-part-two/
The CLI specification actually makes stronger guarantees. The CLI guarantees that reads and writes of variables of value types that are the size (or smaller) of the processor's natural pointer size are atomic; if you are running C# code on a 64 bit operating system in a 64 bit version of the CLR then reads and writes of 64 bit doubles and long integers are also guaranteed to be atomic. The C# language does not guarantee that, but the runtime spec does. (If you are running C# code in some environment that is not implemented by some implementation of the CLI then of course you cannot rely upon that guarantee; contact the vendor who sold you the runtime if you want to know what guarantees they provide.)
Another subtle point about atomic access is that the underlying processor only guarantees atomicity when the variable being read or written is associated with storage that is aligned to the right location in memory. Ultimately the variable will be implemented as a pointer to memory somewhere. On a 32 bit operating system, that pointer has to be evenly divisible by 4 in order for the read or write to be guaranteed to be atomic, and on a 64 bit operating system it has to be evenly divisible by 8.

- 25,590
- 9
- 67
- 77

- 50,833
- 6
- 93
- 125
-
Eric does say that "The C# language does not guarantee that." w.r.t. 64-bit values and atomicity... Only under one specific CLI is this a possibility. Pedantic; but the OP doesn't specify which CLI... – Peter Ritchie Jul 31 '12 at 17:59
From the CLI specifications you can get here:
"A conforming CLI shall guarantee that read and write access to properly aligned memory locations no larger than the native word size (the size of type native int) is atomic…”
Section 12.5 from the C# specification here:
“Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.” Also: “…there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.”
Make the increment operation atomic with this.

- 7,664
- 5
- 31
- 37
-
1Except in the Interlocked class, which does have atomic increment, decrement, and several others. – David Yaw Jul 31 '12 at 17:22