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
c.stdlib.12— C11 threads and atomics.c.os.4— processes (fork/exec) for the platform view.
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
- Processes: stronger isolation, more overhead.
- Threads: cheap sharing, but you must synchronize.
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
- Sharing mutable state without synchronization.
- Assuming a variable update is atomic when it is not.
- Forgetting to join threads (or detaching them).
- Sharing a pointer to a thread's local (stack) variable after it exits.
Undefined Behavior
- A data race on a non-atomic object is UB.
VERIFIED - Using an object after its lifetime (e.g., a joined thread's stack variable).
VERIFIED
Portability
<threads.h>is C11 but optional. POSIX pthreads are widely available.
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
- Use threads for parallel CPU work and concurrent I/O.
- Use processes when isolation is more important than sharing.
- Make shared state explicit and protected.
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.
Related Concepts
c.conc.2— data races.c.conc.3— mutexes.c.stdlib.12— threads.h.
References
- ISO C §7.26 (threads.h), POSIX pthreads.
Verification
- Threads share address space; processes do not.
VERIFIED - Data race on non-atomic object is UB.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Processes vs. threads
- [ ] Shared address space
- [ ] Shared state
- [ ] Thread creation/join
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.conc.process | 0 | 5 |
| c.conc.thread | 0 | 6 |
| c.conc.shared-state | 0 | 6 |