CSS Optional — Paper-II, Section-A · P-II.III · Listed as a standalone topic in the CS-for-CSS book's OS chapter. [Process Management] covers threads only as one section; this page is the full treatment — models, libraries, and threading issues — since thread-vs-process and threading-model questions appear in their own right (2022, 2025).

1. What a Thread Is

A thread is the basic unit of CPU utilization — the smallest sequence of instructions the scheduler can dispatch independently. A process is the container (address space + resources); a thread is a path of execution inside that container. A traditional (heavyweight) process has exactly one thread; a multithreaded process has several threads sharing one address space.

Motivation: most real applications are naturally parallel — a word processor simultaneously accepts keystrokes, renders the display, and runs a spell-checker. Creating a separate process for each of these would mean three address spaces and expensive IPC; three threads share one address space and communicate through ordinary variables.

2. Per-Thread vs Shared Resources (the core exam fact)

Each thread has its OWN All threads SHARE (from the parent process)
Program Counter (PC) Code / text section
Register set Data section (global variables)
Stack (local variables, call frames) Heap (dynamically allocated memory)
Thread ID (TID) Open files and I/O descriptors
Thread state + priority Signals and signal handlers

The single sentence to memorize: threads share code, data, heap and files; they keep their own PC, registers and stack. Almost every thread-vs-process question can be answered from this line plus its consequences.

3. Thread vs Process — Comparison

Aspect Process (heavyweight) Thread (lightweight)
Address space Own, fully independent Shared with sibling threads
Creation cost High — new PCB, page tables, file descriptor table Low — only PC, registers, stack allocated
Context switch Expensive — address space switch, TLB flush Cheap — no address-space change if same process
Communication Needs explicit IPC (shared memory, message passing) Direct, via shared variables
Protection Isolated — one crash can't corrupt another No isolation — one bad pointer corrupts the whole process
Synchronization need Only where memory is deliberately shared Constant — sharing is the default (see [Process Synchronization])

The trade-off to state in an answer: threads buy speed and easy sharing at the cost of protection. Chrome deliberately uses a process per tab rather than a thread, precisely so one tab's crash cannot take down the browser.

4. Benefits of Multithreading (four standard headings)

  1. Responsiveness — a program keeps serving the user even while one thread blocks on a long operation (the UI thread stays alive while a download thread waits on I/O).
  2. Resource sharing — threads share memory and files by default; no shmget-style setup, no kernel-mediated message passing.
  3. Economy — allocating and context-switching a thread is far cheaper than a process, because almost no new state is created.
  4. Scalability / parallelism — a multithreaded process can genuinely run on several cores at once; a single-threaded process cannot, no matter how many cores exist.

5. User-Level vs Kernel-Level Threads

Aspect User-level threads (ULT) Kernel-level threads (KLT)
Managed by A user-space thread library; kernel is unaware of them The OS kernel directly
Switching speed Very fast — no mode switch into the kernel Slower — every switch is a system call / mode switch
Blocking behaviour One blocking system call blocks the ENTIRE process Only the calling thread blocks; siblings continue
True parallelism Not possible on its own — kernel schedules one entity Possible — each thread schedulable onto a separate core
Portability High — implemented purely as a library Depends on OS support

Only kernel threads are schedulable onto physical cores. This single fact answers every "how many threads can run in parallel?" numerical.

6. Multithreading Models

graph TD
    subgraph "Many-to-One"
    U1["User threads"] --> K1["1 Kernel thread"]
    end
    subgraph "One-to-One"
    U2["User thread"] --> K2["Kernel thread"]
    end
    subgraph "Many-to-Many"
    U3["m User threads"] --> K3["n Kernel threads (n <= m)"]
    end