Threads Birrell¶
An Introduction to Programming with Threads — Andrew D. Birrell, January 6, 1989.
Introduction¶
A thread is a single sequential flow of control.
Multiple threads means a program has multiple points of execution at any instant, one per thread.
Threads executing within a single address space can read and write the same memory locations.
Off-stack (global) variables are shared among all threads.
Threads are lightweight — creation, destruction, and synchronization primitives are cheap.
Why Use Concurrency?¶
The alternative is multiple separate processes in separate address spaces — expensive to set up, and inter-address-space communication costs are high even with shared segments.
Threads simplify I/O by treating device requests as sequential (suspending the invoking thread until completion) while the program does other work in other threads.
Threads can defer work — e.g., return to the caller before re-balancing a tree. This is powerful even on a uniprocessor.
Reducing latency improves responsiveness even if total work is the same.
Design of a Thread Facility¶
Four major mechanisms: thread creation, mutual exclusion, waiting for events, and alert (getting a thread out of an unwanted long-term wait).
Thread Creation¶
A thread is created by calling Fork, giving it a procedure and an argument record. REFANY is a dynamically typed pointer to garbage-collected storage.
TYPE Thread;
TYPE Forkee = PROCEDURE(REFANY): REFANY;
PROCEDURE Fork(proc: Forkee; arg: REFANY): Thread;
PROCEDURE Join(thread: Thread): REFANY;
The following executes a(x) and b(y) in parallel, assigning the result of a(x) to q:
VAR t: Thread;
t := Fork(a, x);
p := b(y);
q := Join(t);
Most forked threads are permanent daemon threads, have no results, or communicate results via synchronization rather than
Join.If a thread’s initial procedure returns with no subsequent
Join, the thread quietly evaporates.
Mutual Exclusion¶
The simplest thread interaction is through shared memory access.
Mutual exclusion (critical sections) ensures only one thread executes a particular region of code at a time.
Achieve mutual exclusion by associating variables with a mutex and accessing them only from a thread holding the mutex (inside a
LOCKclause).
TYPE Mutex;
LOCK mutex DO ... statements ... END;
Condition Variables¶
The shared memory accessed inside the LOCK clause is the scheduled resource; the scheduling policy is one thread at a time.
TYPE Condition;
PROCEDURE Wait(m: Mutex; c: Condition);
PROCEDURE Signal(c: Condition);
PROCEDURE Broadcast(c: Condition);
A thread locks the mutex and examines shared data. If the resource is available, it continues. If not, it unlocks the mutex and blocks by calling
Wait.Another thread later makes the resource available and awakens the waiting thread via
SignalorBroadcast.
Alerts¶
Alerts are the exception mechanism for threads — used to interrupt a thread stuck in an unwanted long-term wait.
Deadlocks¶
The most effective rule for avoiding deadlocks: apply a partial order to mutex acquisition. For any pair of mutexes {M1, M2}, every thread that needs both must lock them in the same order (e.g., always M1 before M2). This completely avoids mutex-only deadlocks, though condition variables can introduce additional deadlock scenarios.
Complexity¶
Spurious wake-ups, lock conflicts, and starvation add complexity to concurrent programs.