C Mastery / Processes, Threads, and Shared State
Part 10 — Concurrency

Processes, Threads, and Shared State

This chapter introduces concurrency in C: processes vs. threads, and the shared state that makes threads both powerful and dangerous.

Why This Matters

Concurrency is how modern software uses multiple cores and handles many tasks at once. Processes isolate memory; threads share it. The choice between them — and the discipline around shared state — determines correctness and performance.

Prerequisites

Core Concept

Processes

A process has its own address space. Processes communicate only through explicit IPC (pipes, shared memory, sockets). Isolation is strong; a crash in one does not corrupt another.

Threads

Threads run within one process and share the same address space. They communicate by reading/writing shared memory, which is fast but requires synchronization to avoid data races.

Shared state

Any object accessible by multiple threads is shared state. Uncoordinated concurrent access to a non-atomic object is a data race (UB). The discipline of managing shared state is the heart of thread programming.

Examples

Creating threads (C11 threads.h)

#include <threads.h>
#include <stdio.h>

int worker(void *arg)
{
    int id = *(int *)arg;
    printf("thread %d\n", id);
    return 0;
}

int main(void)
{
    thrd_t t1, t2;
    int a = 1, b = 2;
    thrd_create(&t1, worker, &a);
    thrd_create(&t2, worker, &b);
    thrd_join(t1, NULL);
    thrd_join(t2, NULL);
    return 0;
}

A data race (BUG)

#include <threads.h>

static int counter = 0;

int increment(void *arg)
{
    (void)arg;
    for (int i = 0; i < 100000; i++)
        counter++;        /* data race: not atomic, not synchronized */
    return 0;
}

counter++ is read-modify-write; two threads doing it concurrently lose updates and produce UB.

How It Works

Threads share code, data, and heap, but each has its own stack and registers. The OS scheduler switches between threads, which may run truly in parallel on multiple cores. Without synchronization, interleavings are arbitrary.

Variations

Processes vs. threads trade-off

Platform APIs

C11 <threads.h> is portable but thin. POSIX pthreads (c.os.8) and Win32 threads are richer. They map to the same underlying concepts.

Common Mistakes

Undefined Behavior

VERIFIED

Portability

Under the Hood

Each thread has its own stack and register context; the kernel schedules them. Shared memory is the process's address space, so loads/stores to shared locations must be ordered (atomics/mutexes) for correctness.

Practical Usage

Exercises

1. Create two threads and pass each a distinct argument; join them. 2. Write a data race and observe the lost updates (run many times). 3. Explain the difference between processes and threads in terms of memory.

Deep Challenge

Explain why counter++ is not atomic even on a single-core machine, and describe the possible interleavings. Then show how an atomic counter fixes it.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.conc.process05
c.conc.thread06
c.conc.shared-state06