Concurrency & Parallel Processing (Senior) sample questions with answers
10 questions from the Concurrency & Parallel Processing (Senior) practice bank, spread across its domains. Pick your answer, then open the explanation to see why each option is right or wrong.
- Question 1Correctness
A real-time system has three tasks. Low-priority L holds mutex M. High-priority H wakes up and blocks on M. Medium-priority Med, which never touches M, becomes ready and runs for a long time. What is happening, and what is the standard fix?
- A
Deadlock between H and L; acquire M with a timeout in H
- B
Starvation of Med; give Med a time slice so it yields to L
- C
Priority inversion; give L H's priority via inheritance
- D
A convoy; replace M with a spinlock so H acquires it faster
Show the answer and explanation
Answer: C
H effectively runs at L's priority because it is waiting on L, and Med preempts L, so a medium-priority task delays the highest-priority one for an unbounded time. With priority inheritance, L temporarily inherits H's priority while H waits on M, so Med can no longer preempt it. The priority ceiling protocol is the other common remedy.
Why the other options are wrong
A. There is no cycle: L would release M if it could run.
B. Med is the one running; the problem is that it delays H indirectly.
D. Spinning in H would only waste CPU while L still cannot run.
- A
- Question 2Parallel and Asynchronous Processing
What does this print?
import asyncio async def hog(out): out.append("hog start") s = 0 for i in range(3_000_000): s += i out.append("hog end") async def tick(n, out): out.append(f"tick {n}") await asyncio.sleep(0) out.append(f"tick {n} again") async def main(): out = [] await asyncio.gather(tick(1, out), hog(out), tick(2, out)) print(out) asyncio.run(main())- A
tick 1, tick 2, hog start, tick 1 again, tick 2 again, hog end - B
tick 1, hog start, hog end, tick 2, tick 1 again, tick 2 again - C
tick 1, tick 1 again, hog start, hog end, tick 2, tick 2 again - D
hog start, hog end, tick 1, tick 2, tick 1 again, tick 2 again
Show the answer and explanation
Answer: B
gather wraps each coroutine in a task and they start in argument order. tick 1 runs until sleep(0) yields. hog then runs its whole loop without any await, so "hog end" follows "hog start" directly, and every other task waits. Then tick 2 starts, and the resumed ticks finish. Cooperative scheduling only switches at awaits.
Why the other options are wrong
A. hog never awaits, so nothing can run between its start and end.
C. tick 1 yields at sleep(0), letting the next task start first.
D. gather schedules the tasks in argument order, so tick 1 starts first.
- A
- Question 3Foundations
What does this print?
import threading c = threading.Condition() ready = False with c: r = c.wait_for(lambda: ready, timeout=0.01) print(r)- A
None, because wait_for returns nothing on timeout - B
False, the predicate's value when the timeout expired - C
True, because wait_for returns True once the wait finishes - D
It raises
TimeoutErrorafter 10 ms
Show the answer and explanation
Answer: B
Condition.wait_for(predicate, timeout)loops onwait()until the predicate is true or the timeout passes, then returns the predicate's last value. Here nothing setsready, so it printsFalse(verified on Python 3.13). Checking the return value is how you distinguish "condition met" from "gave up".Why the other options are wrong
A. wait_for returns the last value of the predicate, not None.
C. Only
Event.waithas that shape; wait_for reports the predicate.D. Condition waits never raise on timeout; they report it through the return value.
- A
- Question 4Languages and Design
In Node.js, code running in separate
worker_threadsworkers can execute JavaScript at the same time on different CPU cores, each worker with its own event loop.- A
True
- B
False
Show the answer and explanation
Answer: A
Each
worker_threadsworker runs in its own OS thread with its own V8 isolate and event loop, so CPU-bound work in different workers runs in parallel. Workers don't share ordinary JS objects; they communicate withpostMessage(structured clone or transfer) or share raw memory throughSharedArrayBuffer.Why the other options are wrong
B. Workers are real OS threads, unlike async functions, which all share the main event loop.
- A
- Question 5Correctness
What does this print in Node.js?
const ia = new Int32Array(new SharedArrayBuffer(4)); ia[0] = 9; const r = Atomics.compareExchange(ia, 0, 5, 7); console.log(r, ia[0]);- A
5 7 - B
9 9 - C
false 9 - D
7 9
Show the answer and explanation
Answer: B
The slot holds 9 and the expected value is 5, so the compare fails and 7 is not written.
compareExchangereturns the value it found (9), so the caller detects failure by seeing that the return value differs from the expected 5. Verified in Node 24.Why the other options are wrong
A. That would require the slot to hold the expected value 5, but it holds 9.
C. compareExchange returns the old value, not a boolean success flag.
D. The replacement value is never returned; the old value is.
- A
- Question 6Parallel and Asynchronous Processing
An event-loop image service must also generate thumbnails, which takes about 200 ms of CPU each. What is the best design?
- A
Declare the thumbnail function async so the event loop can interleave it with requests
- B
Send thumbnail jobs to a bounded worker pool and await their results
- C
Run each thumbnail on a new thread spawned per request
- D
Raise the loop's priority so it preempts the thumbnail work
Show the answer and explanation
Answer: B
CPU-bound work must leave the event-loop thread. A bounded pool of worker threads or processes (sized to the cores, and processes where a GIL prevents parallel threads) lets the loop await results while still handling I/O. The bound on the pool and its queue keeps overload from exhausting memory and turns it into backpressure or explicit rejection.
Why the other options are wrong
A. async doesn't add yield points inside CPU-bound code.
C. Unbounded threads collapse under load; a bounded pool is safer.
D. Priority doesn't help when the CPU work runs on the loop thread.
- A
- Question 7Foundations
With Java
wait/notifyand PythonCondition, a thread woken bynotify()runs immediately, before the notifying thread continues, so the condition it waited for is guaranteed still true.- A
True
- B
False
Show the answer and explanation
Answer: B
Under Hoare semantics the signalled thread takes over the monitor at once and the condition is guaranteed to hold. Java, Python, pthreads and Go use Mesa semantics:
notify()only moves a waiter to the "ready" state; the notifier keeps running and other threads may enter first. The woken thread must re-check its predicate in a loop.Why the other options are wrong
A. That describes Hoare semantics, which these languages do not use.
- A
- Question 8Languages and Design
Placing an unbounded queue between a fast producer and a slow consumer gives the system backpressure, because the queue absorbs the difference in rates.
- A
True
- B
False
Show the answer and explanation
Answer: B
An unbounded queue hides a rate mismatch until memory or latency explodes. Backpressure needs a signal back to the producer: a bounded queue whose
putblocks or fails, credit-based flow control, or a reactiverequest(n)protocol. Buffers smooth bursts; they cannot fix a sustained mismatch.Why the other options are wrong
A. Absorbing the difference forever is the opposite of pushing back; the producer is never slowed.
- A
- Question 9Correctness
Two PostgreSQL transactions running at READ COMMITTED update rows 1 and 2, but in opposite orders. They can deadlock, and PostgreSQL resolves it by aborting one of them.
- A
True
- B
False
Show the answer and explanation
Answer: A
UPDATE acquires row-level locks regardless of isolation level. If transaction 1 locks row 1 and transaction 2 locks row 2, each then waits for the other's row. After
deadlock_timeoutPostgreSQL detects the cycle and aborts one transaction with SQLSTATE 40P01. The documented prevention is to lock rows in a consistent order in every transaction.Why the other options are wrong
B. Isolation level does not prevent row-lock deadlocks; UPDATE takes row locks at every level.
- A
- Question 10Parallel and Asynchronous Processing
How does io_uring differ from epoll for a server reading from sockets?
- A
io_uring only notifies readiness, but with less latency than epoll
- B
Requests go in a shared submission ring; results come back on a completion ring
- C
io_uring creates a dedicated kernel thread for each socket and performs blocking reads there
- D
io_uring needs blocking sockets, while epoll needs non-blocking ones
Show the answer and explanation
Answer: B
io_uring uses two ring buffers shared between user space and the kernel: the program places operations (read, write, accept, and more) in the submission queue, and the kernel posts results to the completion queue. Because it is completion-based, it can batch many operations per system call and also handle regular-file I/O asynchronously, which epoll cannot.
Why the other options are wrong
A. io_uring performs the operation itself and reports completion.
C. It is ring-based and doesn't create a thread per descriptor.
D. The difference is completion versus readiness, not socket mode.
- A
Practise all 450 CONC questions
Start with the free 15-question diagnostic. It shows where to focus, and your results carry over if you sign up.