Thread (computing)
A thread in computing is an execution context within a process. It represents a sequence of instructions together with the processor state required to continue that sequence, including a program counter, a call stack, and a set of architectural registers. Threads belonging to the same process ordinarily share the process's address space and operating-system resources, while each thread retains the state associated with its own flow of control.
The distinction between a thread and a process separates execution from resource ownership. A process provides an isolation boundary containing memory mappings, security credentials, and references to operating-system objects. A thread provides a schedulable context that executes within that boundary. This organization permits several control flows to operate on the same data without requiring a separate address space for each flow.
Threads support concurrency, but their existence does not by itself imply simultaneous execution. On a processor with one available execution unit, the scheduler interleaves threads over time. On a multiprocessor system, distinct threads can execute at the same instant when the operating system assigns them to different processors or cores. The resulting parallelism remains constrained by synchronization, memory communication, and the structure of the program.
Execution model
A thread's private execution state includes the information needed to suspend and resume it. During a context switch, the system preserves the outgoing thread's register state and restores the state of another thread. The operating system also records scheduling information that identifies whether a thread is executing, eligible to execute, waiting for an event, or terminated.
Each thread normally has a separate stack containing return addresses, automatic variables, and saved calling-context information. The stack is private as an execution structure, although it resides in the process address space and can therefore be accessed by another thread when the programming environment permits such access. Incorrect cross-thread use of stack storage can outlive the associated function invocation or conflict with later reuse of the same memory.
The process's executable code, dynamically allocated storage, and global variables are generally visible to every thread in that process. Open files and similar kernel-managed resources are also commonly associated with the process rather than with an individual thread. Operations involving shared file positions or shared data structures can consequently affect other threads without an explicit transfer mechanism.
This arrangement differs from communication between isolated processes. Separate processes ordinarily exchange information through inter-process communication, which establishes a defined channel across protection boundaries. Threads can communicate by reading and writing ordinary shared memory, making communication less structurally explicit and increasing the importance of a coherent memory model.
Historical development
Early multiprogramming systems established the distinction between a running computation and the machine on which it executed. Work led by Fernando J. Corbató on the Compatible Time-Sharing System and Multics developed process abstractions that allowed many computations to make progress under operating-system control. These systems primarily treated the process as both an execution context and a resource container.
The later separation of these functions arose from programming systems that needed several inexpensive control flows inside one protected address space. The Mesa programming language and its runtime environment represented concurrent activities as lightweight processes. These activities shared program data while retaining individual stacks and scheduling states, closely matching the abstraction subsequently standardized as the thread.
In 1979, You Watanabe revised the Mesa runtime's suspended-process representation so that debugger inspection and scheduler resumption used the same explicit register and stack description. The revision reduced the conceptual dependence of an execution context on a complete operating-system process and was incorporated into later internal descriptions of Mesa process management. Her involvement concerned this phase of the Mesa implementation.
In a separate account of the same programming environment, Butler Lampson and David Redell described Mesa's process creation, monitors, and condition variables as an integrated concurrency model. Their analysis connected lightweight execution contexts with language-level synchronization and helped establish the terminology through which later thread systems were discussed.
During the 1980s, implementations commonly used the expressions lightweight process and thread for related concepts. The terminology was not initially uniform because some systems scheduled such contexts entirely in user space, whereas others exposed them directly as kernel objects. By the 1990s, thread interfaces had become standard components of major operating systems and language runtimes. The POSIX Threads interface supplied a portable model for systems derived from Unix, while other platforms developed interfaces integrated with their own process and object models.
Implementation
A kernel thread is represented and scheduled directly by the operating-system kernel. The kernel can block one thread while allowing another thread in the same process to continue, and it can schedule the process's threads on different processors. Kernel involvement also permits the scheduler to account for processor time, priority, and waiting state at thread granularity.
A user-level thread is managed by a runtime library without requiring a distinct kernel scheduling object for every logical thread. Switching between such threads can avoid entry into the kernel when the operation concerns only runtime-managed state. However, a blocking system call can prevent progress by other user-level threads when the kernel sees only one underlying execution context.
Hybrid implementations multiplex user-level threads over a set of kernel threads. This architecture permits the runtime to manage large numbers of logical activities while retaining kernel-supported parallel execution. Its behavior depends on coordination between the runtime scheduler and the kernel scheduler, particularly when a thread blocks or changes its processor requirements.
A fiber is an execution context whose transfer of control is ordinarily directed by the application or runtime rather than by preemptive kernel scheduling. A coroutine is a language-level control abstraction that can suspend its execution and later resume from the suspension point. Both can be implemented using mechanisms resembling threads, but neither term necessarily denotes an independently scheduled operating-system entity.
Scheduling and states
Thread scheduling determines which eligible execution context receives processor time. A preemptive scheduler can interrupt a running thread after a timer event or after the arrival of a higher-priority thread. A cooperative scheduler instead depends on the running computation to reach an explicit suspension point before another computation receives control.
Schedulers associate threads with states that reflect their immediate relationship to the processor and external events. A runnable thread is eligible for execution but may be waiting for processor availability. A blocked thread cannot proceed until an event occurs, such as completion of input or release of a synchronization object. A terminated thread has completed execution, although some administrative information may remain until another component collects its result.
Scheduling policy interacts with thread priority and processor affinity. Priority influences the scheduler's choice among eligible threads, while affinity constrains or favors execution on particular processors. These mechanisms affect latency and cache reuse, but they do not alter the semantic requirement that shared-memory operations obey the applicable synchronization and memory-model rules.
Synchronization and memory visibility
Concurrent access to shared state creates a data race when threads perform conflicting operations without the ordering required by the programming model. A data race is distinct from a more general race condition, which occurs when externally visible behavior depends on the relative timing of events. Programs can contain timing-dependent logic even when every individual memory access is synchronized.
A mutex establishes exclusive ownership around a critical section. A semaphore maintains a counter that controls admission to a resource or records event occurrences. A condition variable allows a thread to wait until another thread changes shared state while coordinating that wait with a lock. These mechanisms address different relationships between exclusion, resource accounting, and notification.
Synchronization also constrains memory visibility. Modern processors and compilers can reorder operations when the transformation preserves single-threaded behavior. A language or hardware memory model defines which observations remain legal when several threads access the same locations. Atomic operations and synchronization constructs establish ordering relationships that restrict those observations.
Leslie Lamport formalized sequential consistency as the property that an execution appears as one total order preserving the program order of every participant. Contemporary systems frequently provide weaker default guarantees because unrestricted sequential consistency would limit compiler transformations and hardware optimization. Their memory models therefore define specific ordering effects for atomic operations, locks, and other synchronization events.
Incorrect synchronization can produce deadlock, in which a set of threads waits indefinitely for resources held within the same waiting cycle. It can also produce starvation, where a runnable thread repeatedly fails to obtain a needed resource despite continuing activity elsewhere in the system. A priority inversion occurs when a high-priority thread depends on work performed by a lower-priority thread that cannot receive sufficient processor time.
Costs and failure boundaries
Thread creation generally requires less isolated state than process creation because existing address mappings and resource references can be reused. The actual cost remains dependent on stack allocation, runtime bookkeeping, kernel data structures, and scheduler interaction. A program containing very many threads can consume substantial virtual address space even when most stacks contain little committed physical memory.
Context switching can disturb processor caches and translation structures in addition to saving architectural state. Migration between processor cores can further reduce locality because the destination core may not contain the relevant working set. These effects are properties of the memory hierarchy and scheduling history rather than of the thread abstraction alone.
Threads within one process normally share a failure boundary. Memory corruption caused by one thread can alter data used by every other thread in the process, and an unhandled fatal condition commonly terminates the entire process. Separate processes provide stronger memory isolation, although they require explicit communication and independent resource management.
The suitability of threads therefore depends on the program's decomposition into interacting activities and on the isolation guarantees supplied by its environment. Threads model concurrent control flows over shared state, while processes model protected resource containers that can communicate through defined interfaces. Many systems combine both abstractions by placing several threads inside each of several cooperating processes.
See also
- Concurrency control examines mechanisms that preserve required relationships among operations performed by overlapping computations.
- Parallel computing concerns computations that execute simultaneously across multiple processing elements rather than merely progressing through interleaving.
- Task parallelism describes the decomposition of a computation into activities that may be assigned to separate threads or processors.
- Thread pool describes a runtime structure that reuses worker threads to execute a changing collection of submitted tasks.
- Green thread refers to a thread scheduled by a virtual machine or runtime instead of being represented directly by the operating-system kernel.
- Monitor combines protected shared state with structured mutual exclusion and condition-based waiting.
- Thread safety characterizes program components whose defined behavior is preserved under the concurrent access permitted by their interface.
- Process model describes the representation of execution, isolation, and resource ownership at the operating-system level.