optional · CSS study notes
Threading
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)
- 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).
- Resource sharing — threads share memory and files by default; no
shmget-style setup, no kernel-mediated message passing. - Economy — allocating and context-switching a thread is far cheaper than a process, because almost no new state is created.
- 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
| Model | Mapping | Strength | Weakness |
|---|---|---|---|
| Many-to-One | Many user threads → one kernel thread | Very efficient thread management in user space | One blocking call blocks all threads; no multicore parallelism (e.g. Green Threads) |
| One-to-One | Each user thread → its own kernel thread | True concurrency; a blocked thread doesn't block others | Thread creation cost is a kernel operation, so the number of threads is usually capped (Linux, Windows) |
| Many-to-Many (M:N) | m user threads multiplexed onto n kernel threads, n ≤ m | Application can create as many threads as it likes; kernel-thread count tuned to cores | Complex to implement in the library and the kernel |
| Two-Level | Many-to-Many, plus the option to bind one user thread permanently to one kernel thread | Flexibility of M:N with guaranteed service for critical threads | Same implementation complexity |
Worked numerical pattern (2025-style): A process has 10 user threads mapped onto 2 kernel threads, on an 8-core machine. Maximum threads running in parallel? → 2. The core count and user-thread count are both distractors; the bottleneck is the number of kernel threads, because only those are schedulable.
7. Thread Libraries
- POSIX Pthreads — a specification (IEEE 1003.1c), not an implementation; may be provided as user-level or kernel-level. Standard on UNIX/Linux/macOS. Key calls:
pthread_create(),pthread_join(),pthread_exit(). - Windows threads — a kernel-level library implementing the one-to-one model.
- Java threads — managed by the JVM, but in practice mapped onto the host OS's thread model; created by extending
Threador implementingRunnable.
8. Threading Issues (a standard short-note question)
- fork() and exec() semantics — should
fork()duplicate all threads of the process, or only the calling thread? UNIX offers both versions; ifexec()is called straight after, duplicating all threads is wasted work since the whole image is replaced anyway. - Signal handling — to which thread should a signal be delivered: the one that caused it, every thread, a designated thread, or a specific one? Synchronous signals (e.g. divide-by-zero) go to the causing thread; asynchronous ones (e.g. Ctrl-C) go to any thread not blocking that signal.
- Thread cancellation — terminating a thread before it finishes. Asynchronous cancellation kills it immediately (risking leaked resources or a half-updated shared structure); deferred cancellation lets the target check a flag at safe cancellation points and exit cleanly — deferred is the safer, preferred approach.
- Thread-local storage (TLS) — per-thread data that survives across function calls, distinct from local variables (which vanish on return) and from shared globals.
- Thread pools — create a fixed set of worker threads at startup and hand them tasks from a queue, instead of spawning a thread per request. Bounds resource consumption and removes creation latency from the critical path.
9. Concurrency vs Parallelism
Concurrency = multiple tasks make progress in overlapping time periods (possible on a single core via interleaving). Parallelism = multiple tasks execute at literally the same instant (requires multiple cores). A single-core machine can be concurrent but never parallel — examiners test this distinction directly.
- Data parallelism: the same operation applied to different subsets of the same data across cores.
- Task parallelism: different threads performing different operations, possibly on the same data.
Amdahl's Law: speedup is capped by the serial fraction S of a program — for N cores. With 25% serial code, even infinite cores give at most 4× speedup. Quote this whenever a question asks about the limits of adding cores.
🎯 CSS Exam Angle
Thread questions come in two shapes: (a) a comparison — thread vs process, ULT vs KLT, concurrency vs parallelism — best answered with a table plus one closing sentence on the trade-off; and (b) a small numerical on the threading model, where the answer is always the kernel-thread count, not the core count or user-thread count. For "benefits of multithreading", name the four standard headings (responsiveness, resource sharing, economy, scalability) and give a one-line example against each rather than writing a paragraph.
📌 Past CSS Paper Frequency
| Year | Question | Marks |
|---|---|---|
| 2022 | Thread vs process — resources created for each | 4 |
| 2025 | Threading-model parallelism (user threads on kernel threads) | Numerical |
| MCQ-recurring | Which resource is NOT shared between threads of a process? (Ans: the stack) | 1 |
⚠️ MCQ Traps
- The stack is NOT shared between threads, but the heap IS. This is the single most-asked thread MCQ and the most commonly reversed answer.
- Thread creation is cheap because it shares the address space — not because a thread "does less work" or "runs faster."
- Pthreads is a specification, not an implementation — so "Pthreads are always user-level threads" is false.
- Many-to-One gives no true parallelism even on a multicore machine — the kernel only sees one schedulable entity.
- A blocking system call blocks the whole process under ULT, but only one thread under KLT — this is the deciding difference in almost every ULT/KLT question.
- Concurrency ≠ parallelism — a single-core system running ten threads is concurrent, not parallel.
- Deferred cancellation, not asynchronous, is the safe default — asynchronous cancellation can leave shared data in an inconsistent state.
📜 Past-Paper Questions & MCQs (CSS 2016–2026)
2022 · Paper-II · Q3(c) · 4 marks
Q. What resources are used when a thread is created? How do they differ from those used when a process is created?
Ans.
- Thread creation needs only its own thread control block, stack, program counter and register set (and thread-local storage). It shares the code, data, heap, open files and address space of its process.
- Process creation needs a new address space (page tables), PCB, memory for code, data, heap and stack, a file table and other resources, and the OS must set all of these up.
- Difference: a thread is "lightweight". It is faster to create and to switch (no change of address space), and threads share data easily. A process is "heavyweight" and well isolated. A fault in one thread can affect the whole process, while a process crash is contained.