Level 1 Workers on a shared queue
A photo service gets uploads faster than it can resize them. Uploads go into a queue, and a few worker threads take them off and do the slow work.
class Pipeline:
def __init__(self, n_workers: int, handler): ...
def submit(self, item: int) -> None: ...
def wait_idle(self) -> None: ...
- The constructor starts
n_workersworker threads (make them daemon threads). submit(item)putsitemat the back of a FIFO queue and returns at once. Many threads may call it at the same time.- Each worker repeatedly takes the item at the front of the queue and calls
handler(item). Up ton_workersitems are handled at the same time, and every item is handled exactly once. wait_idle()blocks until the queue is empty and no worker is in the middle of an item.- Don't hold your lock while calling
handler: it's slow, and the other workers must keep going.
Build the queue yourself from a collections.deque and a threading.Condition (no queue.Queue). Idle workers sleep in wait(); no polling or sleeping in a loop.
done = []
p = Pipeline(1, done.append)
for x in [3, 1, 2]:
p.submit(x)
p.wait_idle()
done # [3, 1, 2]: one worker takes the items in order
1 <= n_workers <= 16. The tests submit a few thousand items from several threads at once.
Show hint
a worker holds the lock only to pop an item and count itself busy, then lets go before calling handler. Keep a busy count next to the deque so wait_idle knows about items that have left the queue but aren't finished.