Unit 4 · Self-check
Unit 4 Self-Check Quiz
Twenty-four questions covering the thread abstraction, threading models, multicore concurrency, POSIX threads, and thread-management strategies. Each question can be expanded to reveal a model answer.
Questions
Multiple choice and short answer
Answer each question before expanding. These span all five lessons of Unit 4.
Question 1
What exactly does a thread share with other threads of the same process, and what does it own privately?
Show answer
Question 2
Which of the following is not a benefit of multithreading over multiple processes?
Show answer
SIGSEGV) terminates the entire process, not just the offending thread. Isolation is a benefit of processes, not threads, and is one of the reasons some systems (for example, Chrome's site-per-process model) choose processes over threads for fault containment.
Question 3
In the many-to-one threading model, what happens when one user-level thread blocks on a system call?
Show answer
Question 4
Which threading model maps each user thread to exactly one kernel thread?
Show answer
NPTL and by Windows. Its cost is that creating a user thread now requires a kernel thread, so thread creation is more expensive than in the many-to-one model, and a program that creates thousands of threads will burden the kernel's scheduler.
Question 5
What is the essential idea of the many-to-many threading model, and what problem does it solve?
Show answer
Question 6
Which of the following best distinguishes task parallelism from data parallelism?
Show answer
Question 7
State Amdahl's Law and explain its implication for multicore scaling.
Show answer
S(n) = 1 / ((1 − p) + p / n)
As n → ∞, the speedup approaches 1 / (1 − p). If 20% of the work is serial, the maximum speedup is fivefold regardless of how many cores are added. The implication is that adding cores to a program with a significant serial fraction yields diminishing returns; serious parallel programs must attack the serial portion (the critical section, the sequential I/O, the startup costs) as well as the parallel portion.
Question 8
Why must a multithreaded program's critical sections be designed with care even when the program is correct on a single core?
Show answer
i++, a pointer update, a check-then-act — are no longer atomic. Two threads can read the same location, both compute the new value, and both write back, losing one update. Additionally, the memory consistency model of the hardware permits stores to be reordered as seen by other cores unless fences or atomic operations are used. Correctness on one core therefore does not imply correctness on multiple cores.
Question 9
Which of the following is the correct signature of pthread_create?
(a) int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
(b) void pthread_create(pthread_t thread, void (*start)(void));
(c) int pthread_create(pthread_t *thread, void *start_routine(void *), void *arg);
(d) pthread_t pthread_create(void *(*start)(void *), int attr);
Show answer
pthread_t that receives the new thread's identifier; a pointer to a thread-attribute object (or NULL for defaults); a function pointer of type void *(*)(void *) — the entry point; and a single void * argument that is passed through to the entry point. The function returns 0 on success and a non-zero error code on failure; it does not set errno. The single void * argument is the standard mechanism for passing multiple values: pack them into a struct and pass a pointer to it.
Question 10
What is the difference between pthread_exit and returning from the thread's start routine?
Show answer
pthread_exit with the return value as its argument, provided the return value is not a pointer to a local variable. The pointer is stored in the thread's exit status and can be retrieved by pthread_join. The distinction matters when a thread must exit from a nested function: returning from the start routine is impossible from a deeper call, so pthread_exit is the only way to terminate the thread with a status. The other distinguishing feature is that returning from main terminates the entire process, whereas pthread_exit from main terminates only the calling thread — the other threads continue. That subtlety has caused many debugging sessions.
Question 11
Which of the following is a correct statement about pthread_join?
Show answer
pthread_join can be called from any thread — not just the main thread — and it blocks until the target thread terminates. After it returns, the target thread's resources are released and a second pthread_join for the same thread is undefined behaviour (in practice, it commonly fails with ESRCH or crashes). If the thread is detached (via pthread_detach or an attribute), pthread_join fails with EINVAL, because a detached thread's resources are released automatically on exit.
Question 12
What are the two cancellation modes in POSIX threads, and how do they differ in their effect on the target thread?
Show answer
PTHREAD_CANCEL_DEFERRED) is the default: a cancellation request is recorded, but the target thread is not terminated until it reaches a cancellation point — a function such as read, write, sleep, pthread_cond_wait, or an explicit pthread_testcancel. This gives the thread a chance to release resources in a cancellation handler. Asynchronous cancellation (PTHREAD_CANCEL_ASYNCHRONOUS) terminates the thread immediately, at any instruction. It is dangerous because the thread may hold a mutex or have allocated memory; the program can deadlock or leak. Deferred cancellation is almost always the right choice.
Question 13
Why is fork problematic in a multithreaded program, and what does POSIX say about it?
Show answer
fork in the child — the other threads are not duplicated. But the child inherits the entire address space, including all the mutexes, semaphores, and condition variables of the parent. If a mutex was held by a thread that did not survive, the child has a locked mutex with no one to unlock it, and any attempt to acquire it in the child deadlocks. POSIX addresses this by specifying only a small set of async-signal-safe functions that may be called between fork and exec in a multithreaded program; anything else is undefined. The clean pattern is: fork, then immediately exec, without touching shared state in between. Alternatively, use pthread_atfork to register prepare/parent/child handlers that restore consistency.
Question 14
What is a thread pool, and what problem does it solve?
Show answer
Question 15
Which of the following is not an appropriate use of thread-local storage (TLS)?
Show answer
errno, PRNG state, scratch buffers, user identity in a server worker, and so on. Using TLS for shared state hides the sharing and creates consistency problems — different threads may see stale values.
Question 16
What is a reentrant function, and why is reentrancy important for thread safety?
Show answer
strtok_r for strtok, localtime_r for localtime, readdir_r for readdir — precisely because the originals use static buffers.
Question 17
What is the difference between thread-safe and asynchronous-signal-safe?
Show answer
write, read, _exit, signal masking functions, and a few others). Most library functions, including printf and malloc, are not on the list.
Question 18
Which thread-synchronisation primitive is the correct tool for making a thread wait until a shared predicate becomes true?
Show answer
Question 19
In the fork/join model, what is the role of the join, and how does it differ from a barrier?
Show answer
Question 20
A program creates 16 threads on a machine with 4 cores. Which of the following is true?
Show answer
Question 21
What is the difference between a mutex and a spinlock, and when is a spinlock appropriate?
Show answer
Question 22
Why is it important to check the return value of pthread_create, and what does a non-zero return mean?
Show answer
pthread_create returns 0 on success and a non-zero error code on failure — it does not set errno, so you cannot rely on perror or strerror(errno). A non-zero return means the thread was not created; the reasons include EAGAIN (insufficient resources — too many threads or not enough memory), EINVAL (invalid attributes), and EPERM (no permission to set the requested scheduling policy). Ignoring the return value is a common bug: the program continues as if the thread existed, and pthread_join on the uninitialised pthread_t produces undefined behaviour.
Question 23
Explain why a thread's stack is normally small (a few megabytes) and what happens if it overflows.
Show answer
pthread_attr_setstacksize). If a thread recurses too deeply or allocates a large local array, the stack overflows. On Linux, a guard page at the end of the stack triggers a SIGSEGV; the default action terminates the entire process. There is no "stack grows into heap" safety net for threads. Deep recursion or large local data should be moved to the heap or the stack size increased.
Question 24
In one or two sentences, state the key insight that makes threads the right abstraction for most concurrent server programs.
Show answer
Ready for the graded quiz?
Once you are comfortable with every question above, take the graded Unit 4 quiz in the LMS. It covers the same material with a mix of multiple-choice, short-answer, and short programming problems.