Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python Module 14

Concurrency and parallelism: threads, processes, interpreters interview questions

Interview questions on “Concurrency and parallelism: threads, processes, interpreters”.

31 questions: 8 warm-ups and 23 with model answers · Levels: Fresher to staff

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher to mid

    What is the difference between concurrency and parallelism, and which of the two does the GIL take away from threads?

    Answered in the lesson Concurrency models and the GIL explained

  2. Fresher to mid

    What do start() and join() do on a threading.Thread, and what happens to a daemon thread when the program exits?

    Answered in the lesson Threads: threading and ThreadPoolExecutor

  3. Fresher to mid

    What is the difference between threading.Lock and threading.RLock, and when do you need an RLock?

    Answered in the lesson Thread safety: locks, events and queues

  4. Fresher to mid

    Why should you pass a list of arguments to subprocess.run() instead of one command string?

    Answered in the lesson Running other programs with subprocess

  5. Fresher to mid

    What does if __name__ == "__main__" do in a script that uses multiprocessing, and why do Windows and macOS need it?

    Answered in the lesson Processes with multiprocessing and pools

  6. Fresher to mid

    When do you collect results with executor.map(), and when with submit() and as_completed()?

    Answered in the lesson concurrent.futures: submit, map, as_completed

  7. Mid to senior

    What is the difference between sys._is_gil_enabled() and the Py_GIL_DISABLED configuration variable, and when do you use each?

    Answered in the lesson Running Python without the GIL (3.14t builds)

  8. Mid to senior

    What does it mean that interpreters created with concurrent.interpreters are isolated, and what is it not?

    Answered in the lesson Subinterpreters with concurrent.interpreters

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Mid to senior

    A nightly report job makes 400 HTTP calls of about 0.25 s each, one after another, then spends 30 s computing statistics in pure Python. A teammate proposes "just use threads". Estimate what threads would save, and say what you would do with each phase.

    What it tests
    Splitting a job into I/O-bound and CPU-bound phases, estimating each with numbers, and matching each phase to a model instead of applying one fix everywhere.
    First step
    Split the job into its two phases and estimate each. The calls take 400 × 0.25 s = 100 s in turn, and their time is almost all waiting, so threads overlap it. The 30 s of pure-Python computing holds the GIL, so threads on the default build cannot shorten it.
    Follow-ups

    From the lesson Concurrency models and the GIL explained

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  2. Senior to staff

    Your team wants to move a CPU-heavy Python service to the free-threaded build of Python 3.14 so that its worker threads run in parallel. What do you check before switching, and how do you show that the switch helped?

    What it tests
    What the free-threaded build changes and what it does not, extension compatibility, thread safety of the service's own code, and measuring speed and memory before deciding.
    First step
    Separate the promise from the preconditions. Threads run Python code in parallel only if the GIL really stays off, so first check every extension module the service imports, because one that is not marked as free-threading safe turns the GIL back on. Then plan a measurement on the real workload, not a micro-benchmark.
    Follow-ups

    From the lesson Concurrency models and the GIL explained

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  3. Mid to senior

    Every two minutes a job must check the status of up to 6,000 pending UPI payments through a provider's API. Each call takes about 400 ms, and the provider allows at most 25 calls in flight. Design the threading part: how many threads, and how do you handle slow and failed calls?

    What it tests
    Sizing a thread pool from a deadline, a latency and an external limit, and handling timeouts and failures per item without losing the rest of the batch.
    First step
    Work out whether the deadline can be met at all. In turn, 6,000 × 0.4 s is 2,400 s, twenty times the two-minute window. With 25 calls in flight, the batch takes 6,000 ÷ 25 × 0.4 s = 96 s, which fits inside it.
    Follow-ups

    From the lesson Threads: threading and ThreadPoolExecutor

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  4. Mid to senior

    A background thread that uploads log files stopped working a week ago, and nobody noticed: the service kept running and no error reached the monitoring. Explain how that can happen, and how you would make such failures visible.

    What it tests
    Where an exception that ends a thread goes, threading.excepthook, futures as the carrier of errors, and health checks for background work.
    First step
    Start with where the error went: an exception that ends a thread never travels to the thread that started it, so nothing interrupted the service. Then ask where the thread's error report was written, and whether anyone reads that place.
    Follow-ups

    From the lesson Threads: threading and ThreadPoolExecutor

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  5. Senior to staff

    A counter of processed orders is updated from a pool of threads. Every test passes, but in production the daily total is a few hundred short of the database's count. How do you prove that a race causes it, and how do you fix it for good?

    What it tests
    Recognising a lost update, reproducing a race deterministically instead of hoping, and choosing between a lock, a single owner of the data and returning results instead of sharing state.
    First step
    Look for a read-modify-write on shared data, such as total = total + n or a dictionary entry updated in place, and for anything between the read and the write that can wait, such as a log call or a database query. That gap is where another thread reads the same old value.
    Follow-ups

    From the lesson Threads: threading and ThreadPoolExecutor

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  6. Mid to senior

    A booking service runs on a thread pool. Its code checks if seats_left > 0, charges the card, then decrements seats_left. During a burst it sold 3 more tickets than the train had seats. Explain the bug, fix it, and say what changes when the count lives in a database.

    What it tests
    Recognising check-then-act, making the check and the update one atomic step, keeping slow work outside the lock, and carrying the same idea to a database.
    First step
    Point at the gap: several threads can pass the check before any of them decrements, because the card charge in between takes time. The check and the decrement have to become one step that no other thread can interrupt.
    Follow-ups

    From the lesson Thread safety: locks, events and queues

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  7. Senior to staff

    A service that uses two locks freezes about once a week under load: no CPU use, no errors, requests just stop. How do you confirm that it is a deadlock, find the two code paths involved, and prevent it from coming back?

    What it tests
    Recognising the signs of a deadlock, collecting every thread's stack from a live process, lock ordering, and guards that make a regression fail in tests.
    First step
    Collect evidence before restarting: a frozen process that uses no CPU is waiting for something, so record what every thread is waiting for at that moment, then compare those places with the locks each thread holds.
    Follow-ups

    From the lesson Thread safety: locks, events and queues

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  8. Mid to senior

    Design a pipeline that resizes 80,000 product photos of about 3 MB each: one thread reads files, three threads resize, one thread writes the results. How do you bound memory, report failed photos, and stop cleanly on Ctrl+C?

    What it tests
    Producer-consumer design with bounded queues, sizing a queue from memory, per-item error handling, and an orderly shutdown that works with signals arriving in the main thread.
    First step
    Put a bounded queue between each pair of stages and size it from memory. Estimate the worst case first: if the reader can outrun the resizers, an unbounded queue could end up holding every photo it has read, so ask how many photos may wait in memory at once and turn that into a maxsize.
    Follow-ups

    From the lesson Thread safety: locks, events and queues

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  9. Fresher to senior

    A web endpoint runs subprocess.run(f"convert {filename} out.png", shell=True), where filename comes from the upload form. What is wrong with it, and how do you rewrite it so that no file name can change the command?

    What it tests
    Recognising that shell=True with outside input hands that input to a shell, and replacing it with an argument list, validation and the other defences a server needs.
    First step
    Say what the shell does with the string: it reads the whole line as a command, so characters in the file name that the shell treats as syntax change what runs. The fix starts with removing the shell: pass a list, with the file name as one item, so no shell ever reads it.
    Follow-ups

    From the lesson Running other programs with subprocess

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  10. Mid to senior

    A nightly job runs a backup tool with subprocess.Popen(..., stdout=PIPE, stderr=PIPE) and then calls wait(). Most nights it works; on nights with large backups it never finishes. What is happening, and how do you fix it?

    What it tests
    Pipe buffers and the deadlock between a writer and a waiting parent, communicate() and run(), timeouts that kill, and turning a hang into a reported failure.
    First step
    Notice that nobody reads the pipes while the parent waits. A pipe holds only a limited amount of data, so a large backup that prints a lot can fill it and block, while the parent waits for the tool to exit.
    Follow-ups

    From the lesson Running other programs with subprocess

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    A script runs git status with subprocess, splits the output into lines and parses them to find changed files. On some machines it crashes with UnicodeDecodeError, and on others it misses files. Why, and how do you make it reliable?

    What it tests
    Decoding a child's output, locale and translated messages, file names with unusual characters, and preferring a machine-readable output format with explicit decoding.
    First step
    Separate the two failures. The crash comes from decoding: text=True uses the machine's locale encoding, and one byte in a file name that is not valid in it raises UnicodeDecodeError. The missed files come from parsing output meant for people, whose wording, translation and quoting differ between machines and versions.
    Follow-ups

    From the lesson Running other programs with subprocess

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  12. Mid to senior

    A teammate moved a loop over 200,000 small records to ProcessPoolExecutor.map() and the job became ten times slower than the plain loop. What most likely went wrong, how would you measure it, and what would you change?

    What it tests
    Per-task messaging and pickling costs, start-up cost, choosing a chunk size, and measuring before parallelising.
    First step
    Compare the work per record with the cost of one round trip. ProcessPoolExecutor.map() sends one item per message by default, so 200,000 records mean 200,000 pickled tasks and results, all handled by the main process. If a record takes microseconds, the messaging dominates; time the plain loop per record, then the pool with a large chunksize.
    Follow-ups

    From the lesson Processes with multiprocessing and pools

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  13. Mid to staff

    A Linux service ran fine on Python 3.13. On 3.14 its workers see empty configuration, and a lambda passed as the pool's initializer raises PicklingError. What changed, and how do you fix it without forcing the old start method?

    What it tests
    The 3.14 change of the default start method on Linux, what fork let code get away with, and passing state to workers explicitly.
    First step
    Name the change: from Python 3.14 the default start method on Linux is forkserver, not fork. A forked worker was a copy of the parent, so globals filled at run time were there and its initializer was never pickled; a forkserver worker gets its state by importing the script and receives the initializer and its arguments pickled.
    Follow-ups

    From the lesson Processes with multiprocessing and pools

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  14. Mid to senior

    Eight worker processes must keep a shared count of processed files and a shared set of the names of files that failed. Design it: compare at least two approaches and say which one you would ship.

    What it tests
    Process isolation, returning results against shared objects, the cost of Manager proxies and locks, and designing shared state away.
    First step
    Start from isolation: each process changes only its own copies, so a global counter in a worker never reaches the parent. Then question the requirement. If the parent needs the totals only at the end, each task can return what it processed and what failed, and the parent adds them up with no shared state at all.
    Follow-ups

    From the lesson Processes with multiprocessing and pools

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  15. Mid to senior

    A checkout page calls five services in parallel on a ThreadPoolExecutor and must answer within 800 ms. Payment and stock are required; offers, delivery estimate and recommendations are optional. Design the waiting, the timeouts and the failure handling.

    What it tests
    Deadlines across a batch with wait(), required against optional calls, what timeouts do to running calls, and keeping abandoned work from exhausting the pool.
    First step
    Separate the deadline for the page from the timeouts of each call. Submit all five, then wait(futures, timeout=budget) once, where budget is what is left of the 800 ms. After it returns, a missing required result fails the request, and a missing optional one is replaced by a default.
    Follow-ups

    From the lesson concurrent.futures: submit, map, as_completed

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  16. Mid to senior

    A batch job saves each result to a database from an add_done_callback() on its future. Some results never reach the database, the job reports success, and the request error metrics show nothing. Explain what happened and redesign it.

    What it tests
    Where done callbacks run, what happens to an exception raised in one, and collecting results so that every failure reaches the caller.
    First step
    Find out which thread runs the callback and what happens to an exception raised inside it. Then follow one failed database write from the callback to the job's exit status, and see where the error stops travelling.
    Follow-ups

    From the lesson concurrent.futures: submit, map, as_completed

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  17. Mid to staff

    A job calls executor.map(parse, open("huge.log")) on a 40 GB log file and runs out of memory before it prints a single result. Explain why, and fix it for Python 3.14 and for Python 3.12.

    What it tests
    How Executor.map() submits its inputs, estimating the memory of pending futures, buffersize in 3.14, and a bounded submit loop for older versions.
    First step
    Read what map() does with its input: before it returns the first result it collects the whole iterable and submits one call per item, so every line of the file becomes a pending future holding the line. Estimate the number of lines, multiply by the size of a line plus a future, and compare it with the memory available.
    Follow-ups

    From the lesson concurrent.futures: submit, map, as_completed

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  18. Senior to staff

    A service was moved to python3.14t so that its 16 worker threads compute in parallel, but its CPU use never goes above one core, and a warning about the GIL appears once in the start-up logs. Diagnose it and describe the fix.

    What it tests
    How an extension module turns the GIL back on, confirming it from inside the process, and the options from upgrading to isolating the module.
    First step
    Confirm the state before guessing: log sys._is_gil_enabled() after start-up. If it is True on a free-threaded build, something turned the GIL on, and the start-up warning names the culprit: an extension module that has not declared it can run without the GIL, which makes Python enable the GIL for the whole process as it loads.
    Follow-ups

    From the lesson Running Python without the GIL (3.14t builds)

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  19. Mid to senior

    Eight threads share one generator that yields job ids. On the default build a job occasionally fails with "ValueError: generator already executing"; on the free-threaded build some jobs seem to run twice. Explain both symptoms and fix the design.

    What it tests
    Why generators and other iterators are not safe to share between threads, how the GIL hides races, and the designs that hand out work safely.
    First step
    Separate the two symptoms. A generator's body is Python code that one thread runs at a time, so a second next() while the first is inside the body raises ValueError. Without the GIL two threads really run at the same instant, so collisions are far more frequent, and the guide warns that iterators shared between threads may give items twice or skip some.
    Follow-ups

    From the lesson Running Python without the GIL (3.14t builds)

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  20. Senior to staff

    After switching a billing service to python3.14t, invoice totals computed in worker threads round differently, although no code changed. The threads are started inside a decimal.localcontext() block. What changed, and how do you make the service behave the same on both builds?

    What it tests
    The thread_inherit_context default of the free-threaded build, context variables in threads, and making behaviour explicit instead of relying on build defaults.
    First step
    Name the default that differs: on the free-threaded build, the flag thread_inherit_context is on, so a new thread starts with a copy of its creator's context, decimal precision and rounding included. On the default build it is off and threads start with an empty context, the standard 28 digits, which is what the service's totals had silently relied on.
    Follow-ups

    From the lesson Running Python without the GIL (3.14t builds)

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  21. Mid to senior

    A service scores incoming documents with a pure-Python model that takes about 400 ms of CPU per document, using a ProcessPoolExecutor of 8 workers. A colleague proposes InterpreterPoolExecutor instead. How do you decide, and what would you measure first?

    What it tests
    The trade-offs between processes and interpreters: start-up, memory, data passing and extension support, decided with measurements rather than preference.
    First step
    List what the workers import and test each import in a fresh interpreter first: one dependency that refuses to load in a subinterpreter ends the discussion. Then compare what interpreters would save, start-up time and memory per worker, with what the job spends, 400 ms of CPU per document.
    Follow-ups

    From the lesson Subinterpreters with concurrent.interpreters

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  22. Mid to senior

    After moving a batch job to InterpreterPoolExecutor, every task fails with an ImportError saying a module "does not support loading in subinterpreters", although the same import works in the main interpreter. Explain why, and what your options are.

    What it tests
    Single-phase and multi-phase extension modules, why isolation needs module support, and choosing a fallback.
    First step
    Read the error as a property of the extension module rather than of your code. Find out which module raised it, which of your imports pulls it in, and whether a newer release of it supports multiple interpreters, before you change any of the job's own code.
    Follow-ups

    From the lesson Subinterpreters with concurrent.interpreters

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  23. Senior to staff

    Eight interpreters each need fast read access to a 2 GB table of numbers, and one of them must update a few entries every minute. Design how the data is held and shared, and say what can go wrong.

    What it tests
    What can be shared between interpreters, memoryviews over one buffer, coordinating writers without shared locks, and estimating the memory saved.
    First step
    Rule out copies first: eight copies of 2 GB need 16 GB. Keep the numbers in one buffer, a bytearray or shared memory, and give every interpreter a memoryview of it, since memoryviews share their data; then design how the one writer changes entries without readers seeing half-written values.
    Follow-ups

    From the lesson Subinterpreters with concurrent.interpreters

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes

All Python interview questions · The Python track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.