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.

  1. 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.

  2. 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.

  3. 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 TimeoutError after 10 ms

    Show the answer and explanation

    Answer: B

    Condition.wait_for(predicate, timeout) loops on wait() until the predicate is true or the timeout passes, then returns the predicate's last value. Here nothing sets ready, so it prints False (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.wait has that shape; wait_for reports the predicate.

    • D. Condition waits never raise on timeout; they report it through the return value.

  4. Question 4Languages and Design

    In Node.js, code running in separate worker_threads workers 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_threads worker 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 with postMessage (structured clone or transfer) or share raw memory through SharedArrayBuffer.

    Why the other options are wrong

    • B. Workers are real OS threads, unlike async functions, which all share the main event loop.

  5. 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. compareExchange returns 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.

  6. 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.

  7. Question 7Foundations

    With Java wait/notify and Python Condition, a thread woken by notify() 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.

  8. 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 put blocks or fails, credit-based flow control, or a reactive request(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.

  9. 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_timeout PostgreSQL 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.

  10. 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.

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.

Go to CONC