What operations are atomic in C?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In the .NET context, an atomic operation is one that cannot be observed halfway through by another thread. The tricky part is that not every read, write, increment, or compound expression is atomic, and even when an operation is atomic, that does not automatically make a larger algorithm thread-safe.
Atomic Is Smaller Than Thread-Safe
A single atomic read or write can still be part of a broken multi-step sequence. For example, "read value, add one, write it back" is not automatically atomic as a whole just because each simple access looks innocent.
That is why the question matters: you need to know which individual operations are atomic and when to use stronger tools such as Interlocked or locks.
Simple Reads and Writes
In ordinary .NET code, reads and writes of certain primitive values and references are atomic under the runtime guarantees typically relied upon in C# code. But you should be careful not to generalize that into a claim that all compound manipulations are atomic.
This is not atomic as an increment operation:
Even though it looks small, it is conceptually:
- Read
counter. - Add one.
- Write the result back.
Another thread can interfere between those steps.
Use Interlocked for Common Atomic Updates
When you need an atomic increment, decrement, exchange, or compare-and-swap, use the Interlocked class.
Other common operations include:
- '
Interlocked.Decrement' - '
Interlocked.Add' - '
Interlocked.Exchange' - '
Interlocked.CompareExchange'
These exist specifically because x++ and similar expressions are not enough in multithreaded code.
References and Visibility Are Different Questions
Reference assignment is often described as atomic, but atomicity alone does not solve memory-ordering or visibility questions. One thread may write a value atomically and another may still need the proper synchronization semantics to observe program state correctly.
That is why multithreading discussions often mention both:
- Atomicity.
- Memory ordering or visibility.
If you need both correctness and coordination, Interlocked, volatile, or locks may be part of the design depending on the problem.
Locks Still Matter
Not every problem should be solved with atomic primitives alone. If the operation spans multiple state changes, a lock can be the correct solution.
A lock is more expensive than a single atomic primitive, but it protects larger invariants that Interlocked.Increment alone cannot express.
Volatile Is Not a Universal Replacement
volatile can affect visibility semantics for specific scenarios, but it is not a substitute for every atomic update pattern. If the operation itself must be indivisible, Interlocked or a lock is still the right tool.
Common Pitfalls
- Assuming
x++is atomic because it looks like one operation in source code. - Confusing an atomic primitive with a thread-safe multi-step algorithm.
- Using plain reads and writes where
Interlockedwas actually needed. - Ignoring visibility and ordering after focusing only on atomicity.
- Replacing a necessary lock with atomic primitives even though the real problem spans multiple fields or steps.
Summary
- Atomic means an operation cannot be observed halfway through by another thread.
- Simple-looking compound expressions such as
counter++are not atomic as whole updates. - Use
Interlockedfor common atomic numeric and exchange operations. - Atomicity alone does not guarantee full thread safety or visibility correctness.
- Use locks when the invariant spans more than one atomic step.

