Synchronization Primitives — Series 2

Preview — 3 of 10 questions

What is the output of the following code?

javascript
import asyncio

async def producer(queue, order):
    for i in range(3):
        await queue.put(i)
        order.append(f"produced {i}")

async def consumer(queue, order):
    await asyncio.sleep(0.02)
    for _ in range(3):
        item = await queue.get()
        order.append(f"consumed {item}")

async def main():
    queue = asyncio.Queue(maxsize=1)
    order = []
    await asyncio.gather(producer(queue, order), consumer(queue, order))
    print(order)

asyncio.run(main())
A['produced 0', 'produced 1', 'produced 2', 'consumed 0', 'consumed 1', 'consumed 2'] — the maxsize=1 limit only takes effect once the consumer has started running.
B['consumed 0', 'produced 0', 'consumed 1', 'produced 1', 'consumed 2', 'produced 2']
CThe program deadlocks — a maxsize=1 queue can never actually hold more than one produced item awaiting consumption.
D['produced 0', 'consumed 0', 'produced 1', 'consumed 1', 'produced 2', 'consumed 2']

What is the output of the following code?

javascript
import asyncio

async def worker(queue, results):
    while True:
        item = await queue.get()
        if item is None:
            queue.task_done()
            break
        results.append(item * 2)
        queue.task_done()

async def main():
    queue = asyncio.Queue()
    results = []
    for i in range(3):
        await queue.put(i)

    task = asyncio.create_task(worker(queue, results))
    await queue.join()
    print(results)

    await queue.put(None)
    await task

asyncio.run(main())
A[0, 2, 4]
B[] — queue.join() returns before the worker has processed anything at all.
C[0, 2, 4, 0] — the None sentinel is also doubled and processed as data.
DThe program hangs forever at await queue.join(), since three task_done() calls are never enough to unblock it.

What is the output of the following code?

javascript
import asyncio

async def fails():
    await asyncio.sleep(0.01)
    raise ValueError("boom")

async def succeeds():
    await asyncio.sleep(0.02)
    return "ok"

async def main():
    try:
        async with asyncio.TaskGroup() as tg:
            tg.create_task(fails())
            tg.create_task(succeeds())
    except* ValueError as eg:
        print("caught group:", [str(e) for e in eg.exceptions])

asyncio.run(main())
Acaught group: ['boom', 'ok'] — except* groups every task's outcome, successes included, into one list.
Bcaught group: ['boom']
CValueError: boom propagates uncaught, since except* only catches ExceptionGroup, never a wrapped ValueError directly.
DNothing is printed — TaskGroup silently discards exceptions from failed child tasks.

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.