1. Mid-level

    What is the effect of the GIL in CPython, and what changed with the free-threaded build?

    In traditional CPython the GIL lets only one thread execute bytecode at a time, so CPU-bound work does not benefit from threads. Since Python 3.13 there is an experimental build without the GIL (free-threaded), and it became officially supported in Python 3.14.

    Read the full explanation
  2. Mid-level

    When do you choose threading and when multiprocessing?

    Choose threading for I/O-bound tasks and cheap memory sharing, and choose multiprocessing for CPU-bound tasks that need true parallelism in CPython, with isolated memory.

    Read the full explanation
  3. Junior

    What is an event loop and what is the role of a coroutine in asyncio?

    The event loop manages tasks and runs each coroutine until it reaches an await on an operation that is not ready, then switches to another ready task, all within a single thread.

    Read the full explanation
  4. Mid-level

    What is the difference between a coroutine, a Task and a Future in asyncio?

    A coroutine is a description of asynchronous work that does not run until it is awaited or scheduled, a Task wraps a coroutine and schedules it to run concurrently on the loop, and a Future is a low-level object representing a result that will be completed later (a Task is a subtype of it).

    Read the full explanation
  5. Senior

    What is the practical difference between asyncio.gather and TaskGroup?

    TaskGroup (since 3.11) applies structured concurrency: when a task fails it cancels the rest, collects the errors in an ExceptionGroup, and exits only after all tasks have finished. gather returns the first error (or collects errors with return_exceptions=True) without cancelling the other tasks.

    Read the full explanation
  6. Senior

    How do you handle cancellation in asyncio?

    Cancellation is cooperative: task.cancel() raises CancelledError inside the task at the next await point, and you must clean up resources in finally and then let CancelledError propagate rather than swallowing it.

    Read the full explanation
  7. Mid-level

    How do you run a blocking function inside asyncio?

    Pass it to asyncio.to_thread (or loop.run_in_executor) so it runs in a separate thread without blocking the event loop, and use a ProcessPoolExecutor with run_in_executor for CPU-bound work.

    Read the full explanation
  8. Senior

    What is the role of asyncio.Queue in the producer-consumer pattern?

    It decouples the producer from the consumer, and when maxsize is set the producer pauses at await put() when the queue is full, which gives backpressure and keeps memory bounded.

    Read the full explanation
  9. Mid-level

    When do you need a Lock in concurrent code?

    When several threads or tasks read and modify shared state in multiple steps (read-modify-write), so you must protect the invariant from interleaving.

    Read the full explanation
  10. Mid-level

    What is the concurrent.futures module for?

    It offers a unified interface to submit work to a ThreadPoolExecutor or ProcessPoolExecutor through submit and map, and to follow the results as Futures with as_completed.

    Read the full explanation