concurrency
multithreading
C#
programming
systems_design

Interlocked and volatile

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Interlocked and volatile both appear in multithreaded C# code, but they solve different problems. volatile is about visibility and ordering of reads and writes, while Interlocked is about atomic operations such as incrementing, exchanging, or compare-and-swap on shared values.

What volatile Does

Marking a field as volatile tells the runtime and compiler not to treat reads and writes to that field like an ordinary cached variable access. The goal is to ensure that one thread sees another thread's updates in a timely and correctly ordered way.

csharp
1using System;
2using System.Threading;
3
4class Example
5{
6    private static volatile bool _stop;
7
8    static void Main()
9    {
10        var worker = new Thread(() =>
11        {
12            while (!_stop)
13            {
14            }
15            Console.WriteLine("Worker noticed stop flag");
16        });
17
18        worker.Start();
19        Thread.Sleep(100);
20        _stop = true;
21        worker.Join();
22    }
23}

Here volatile is appropriate because the field is a simple shared flag.

What volatile Does Not Do

volatile does not make compound operations atomic. This is the most important limitation.

csharp
volatile int counter = 0;
counter++;

The increment still means:

  1. read the current value
  2. add one
  3. write the new value

Another thread can interleave with those steps, so volatile alone does not protect counters from race conditions.

What Interlocked Does

Interlocked provides atomic operations on shared values. These operations happen as one indivisible unit from the perspective of competing threads.

csharp
1using System;
2using System.Threading;
3
4class Example
5{
6    private static int _counter;
7
8    static void Main()
9    {
10        Thread t1 = new Thread(Work);
11        Thread t2 = new Thread(Work);
12
13        t1.Start();
14        t2.Start();
15        t1.Join();
16        t2.Join();
17
18        Console.WriteLine(_counter);
19    }
20
21    static void Work()
22    {
23        for (int i = 0; i < 100000; i++)
24        {
25            Interlocked.Increment(ref _counter);
26        }
27    }
28}

This safely increments the shared counter without using a full lock.

Compare-And-Swap With Interlocked.CompareExchange

One of the most powerful Interlocked operations is compare-and-swap.

csharp
1using System;
2using System.Threading;
3
4class Example
5{
6    private static int _initialized;
7
8    static void Main()
9    {
10        if (Interlocked.CompareExchange(ref _initialized, 1, 0) == 0)
11        {
12            Console.WriteLine("Initialization performed once");
13        }
14    }
15}

This sets _initialized to 1 only if it was previously 0, and does so atomically.

When to Use Each One

A simple rule works well:

  • use volatile for simple state flags or references where visibility is the main concern
  • use Interlocked for atomic updates to shared numeric values or references
  • use lock when several operations must be protected together as one critical section

That distinction prevents many incorrect uses of volatile.

lock Is Still Important

Neither volatile nor Interlocked replaces lock for larger invariants.

If several fields must be updated together consistently, or if a read depends on several related values matching each other, a lock is often the right tool.

Interlocked is excellent for small atomic operations. It is not a complete replacement for higher-level synchronization.

Common Pitfalls

The biggest pitfall is using volatile for counters and assuming it prevents lost updates. It does not, because incrementing is not an atomic operation.

Another issue is overusing Interlocked when the code really needs a proper critical section around several related operations.

Developers also treat volatile as a general thread-safety keyword. It is not. It gives visibility guarantees for that field, not full synchronization for the surrounding object state.

Finally, do not mix several concurrency techniques casually without a clear reason. Concurrency bugs are hard enough when the design is consistent.

Summary

  • 'volatile is about visibility and ordering of field access.'
  • 'Interlocked is about atomic operations on shared values.'
  • 'volatile does not make x++ safe.'
  • Use Interlocked for counters, swaps, and compare-and-set patterns.
  • Use lock when a whole sequence of operations must stay consistent together.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.