FIT2100 Chap.7 Threads, Races and Mutual Exclusion
Threads, Races and Mutual Exclusion
Threads separate execution from process resource ownership. Threads in one process share code, data, heap and open resources, while each retains a private program counter, registers and stack. This arrangement supports responsive servers and parallel work, but shared mutable state creates correctness risks.
A race condition exists when an uncontrolled interleaving changes the result.
Compound operations such as incrementing a counter contain a read, computation and write even when they appear as one source line. Mutual exclusion must protect the complete invariant-preserving region. Correct locking also requires a consistent lock object, release on every exit and attention to progress and deadlock.
What this chapter covers
- 01
Thread model
- 02
Shared process resources
- 03
Private thread state
- 04
Thread libraries
- 05
Race conditions
- 06
Critical sections
- 07
Mutual exclusion
- 08
Lock scope and ordering
Worked example · free
Expose a lost update
- +2Thread A reads 10, then thread B also reads 10 before either thread writes.
- +2Both compute private temporary value 11. A writes 11 and B later writes 11, so one logical increment is lost.
- +2The critical section begins before the shared read and ends after the shared write, using the same lock on every path.
Key terms
- Thread
- A dispatchable execution stream with private register context and stack inside a process resource container.
- Shared State
- Mutable data reachable by more than one concurrent execution stream.
- Race Condition
- A defect where the result depends on an uncontrolled ordering of accesses to shared state.
- Critical Section
- The complete code region whose shared accesses must obey a synchronisation rule.
- Mutual Exclusion
- A property allowing at most one execution stream inside a protected critical section at a time.
- Lock Ordering
- A global acquisition order used to prevent circular waits when operations need several locks.
Threads, Races and Mutual Exclusion FAQ
Why are threads cheaper to communicate between?
Threads already share their process address space, so they can exchange data through common memory without a separate inter-process transfer. That convenience also means concurrent access needs an explicit rule whenever at least one thread writes.
Can repeated successful tests prove there is no race?
No. A scheduler may simply avoid the dangerous interleaving during those runs. Safety requires reasoning about all relevant orders and enforcing a synchronisation rule, not observing one favourable timing pattern.
How large should a critical section be?
It must include every access needed to preserve the shared invariant, starting before the first dependent read and ending after the final write. Making it much larger can reduce concurrency, while making it smaller can leave a race.
Why must every exit release the mutex?
An early return or exception that leaves a mutex held can block future threads indefinitely. Use structured cleanup or a design that guarantees release, and test the error path rather than demonstrating only normal completion.
Assessment move
For each shared variable, write the invariant and expand compound updates into reads and writes. Construct one failing interleaving before proposing a lock. Then verify that all access paths use the same lock and every exit releases it. Practise explaining safety separately from progress.
In a lab, make the race observable with repetition or controlled yields, then explain why timing only reveals the bug and does not cause the underlying absence of synchronisation. Make a sharing inventory before analysing code: mark globals, heap objects, open resources and any pointers passed into a new thread, while keeping registers and stacks distinct. For every mutable shared object, state the invariant in plain language.
Expand each source-level update into read, compute and write events, then search for an interleaving that violates that invariant. Do not propose a mutex until the failing schedule identifies the complete critical section. After adding protection, audit all functions that touch the invariant, including error and early-return paths, and check that they use the same lock.
Draw a lock-order graph for operations that require more than one mutex; a consistent order should be visible across every path. Compare safety, progress and bounded waiting separately, because successful final output in one run proves none of them. In experiments, increase repetition or insert controlled yields to expose a race, then remove timing language from the diagnosis.
Record the observed lost update as evidence, but base the proof on the permitted interleaving. Finally, test the repair under the same stress and trigger a failure path to verify that cleanup releases the mutex. Explain any throughput cost in terms of critical-section scope rather than assuming more locking is always safer.
Working through Threads, Races and Mutual Exclusion in FIT2100? Sia is AskSia’s AI Computer Science tutor — ask any FIT2100 Threads, Races and Mutual Exclusion question and get a clear, step-by-step explanation grounded in how FIT2100 is taught and assessed. Read this chapter free, then take your hardest questions to Sia.