liveBook · Manning

Queue

As explained in Grokking Concurrency

A FIFO is a first-in, first-out communication mechanism in which data is processed in the order it arrives.

Imagine a line of tasks waiting for the next available worker. A queue holds them in first-in, first-out (FIFO) order: a worker takes the next task that arrived. When several workers share a queue, they can process different tasks concurrently, so retrieval order does not necessarily determine completion order. section 5.3 section 5.4

A message queue links a task producer to its consumers without requiring them to work at the same moment. It can support asynchronous communication between threads or processes. In a thread pool, the main thread adds tasks; an available worker retrieves one, runs it, then returns for more. Reusing workers avoids creating a thread for every task. section 5.4 section 5.6

Ten tasks, four workers

Put the numbers 0 through 9 into a Python queue, then start four worker threads. Each worker calls `get()` to claim an item and processes it. The workers can claim successive items in queue order while their work overlaps. The queue coordinates who gets the next item; it does not make the work itself sequential. section 5.3

# Chapter 5/thread_pool.py
import time
import queue
import typing as T
from threading import Thread, current_thread

Callback = T.Callable[..., None]
Task = T.Tuple[Callback, T.Any, T.Any]
TaskQueue = queue.Queue

class Worker(Thread):
    def __init__(self, tasks: queue.Queue[Task]):
        super().__init__()
        self.tasks = tasks
 
    def run(self) -> None:
        while True:                                                    #1
            func, args, kargs = self.tasks.get()                       #1
            try:                                                       #1
                func(*args, **kargs)                                   #1
            except Exception as e:                                     #1
                print(e)                                               #1
            self.tasks.task_done()                                     #1
 
class ThreadPool:
    def __init__(self, num_threads: int):
        self.tasks: TaskQueue = queue.Queue(num_threads)               #2
        self.num_threads = num_threads
 
        for _ in range(self.num_threads):                              #3
            worker = Worker(self.tasks)                                #3
            worker.setDaemon(True)                                     #3
            worker.start()                                             #3
 
    def submit(self, func: Callback, *args, **kargs) -> None:
        self.tasks.put((func, args, kargs))
 
    def wait_completion(self) -> None:
        self.tasks.join()                                              #4
 
def cpu_waster(i: int) -> None:
    name = current_thread().getName()
    print(f"{name} doing {i} work")
    time.sleep(3)
 
def main() -> None:
    pool = ThreadPool(num_threads=5)                                   #5
    for i in range(20):                                                #6
        pool.submit(cpu_waster, i)                                     #6
 
    print("All work requests sent")
    pool.wait_completion()
    print("All work completed")
 
if __name__ == "__main__":
    main()

A Python thread-pool implementation shows workers retrieving queued tasks, executing callbacks, and marking tasks done. section 5.4

Choose a queue when independent tasks should be handed to available workers or messages should be exchanged asynchronously. Other communication needs may call for shared memory, pipes, or sockets instead. section 5.4 section 5.6

How Grokking Concurrency covers it

How deep it goes
Explains FIFO task retrieval and illustrates Python worker loops, without detailing the queue’s internal implementation.
Examples
Shows four Python workers processing ten numbered queue items and a thread pool retrieving callback tasks.
What it assumes
Introduces task queues through workers and examples that use Python thread and queue APIs.
What you can do after
Apply it via Python `queue.Queue` to hand incoming tasks to reusable worker threads.
How it connects
Connects queues to message passing, interprocess communication, thread pools, pipes, sockets, and shared memory.

Also explained in

Compare how 24 books explain queue →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Grokking Concurrency. The section links point to the parts of the book it draws on.