Python
Locks
Concurrency
Threading
Synchronization

How Do I Queue My Python Locks?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Python Lock objects protect shared state, but they do not guarantee fairness among waiting threads. If you need first-come-first-served behavior, you need a queue-based coordination pattern. A custom FIFO lock built with Condition and a waiting queue can provide predictable acquisition order.

Why Standard Locks Are Not Queued

threading.Lock only guarantees mutual exclusion. It does not guarantee the next owner is the longest-waiting thread. In high-contention workloads, this can lead to uneven latency.

For many programs that is fine, but queueing matters when fairness and starvation prevention are requirements.

Implement a FIFO Lock

The following lock tracks waiters in arrival order.

python
1import threading
2from collections import deque
3
4
5class FIFOLock:
6    def __init__(self):
7        self._cond = threading.Condition()
8        self._owner = None
9        self._queue = deque()
10
11    def acquire(self):
12        token = object()
13        with self._cond:
14            self._queue.append(token)
15            while self._owner is not None or self._queue[0] is not token:
16                self._cond.wait()
17            self._queue.popleft()
18            self._owner = threading.get_ident()
19
20    def release(self):
21        with self._cond:
22            if self._owner != threading.get_ident():
23                raise RuntimeError("lock released by non-owner")
24            self._owner = None
25            self._cond.notify_all()
26
27    def __enter__(self):
28        self.acquire()
29        return self
30
31    def __exit__(self, exc_type, exc, tb):
32        self.release()

This provides predictable acquisition order under thread contention.

Example Usage

Use like a normal context-managed lock.

python
1import threading
2import time
3
4fifo_lock = FIFOLock()
5shared = []
6
7
8def worker(i):
9    time.sleep(0.01 * (i % 3))
10    with fifo_lock:
11        shared.append(i)
12        time.sleep(0.02)
13
14threads = [threading.Thread(target=worker, args=(i,)) for i in range(10)]
15for t in threads:
16    t.start()
17for t in threads:
18    t.join()
19
20print(shared)

Order reflects lock queue entry timing, not random scheduler outcomes.

Alternative: Queue Work, Not Locks

Often a better pattern is pushing critical work to a single worker thread via queue.Queue.

python
1import queue
2import threading
3
4q = queue.Queue()
5
6
7def worker():
8    while True:
9        item = q.get()
10        if item is None:
11            break
12        print("processing", item)
13        q.task_done()
14
15thread = threading.Thread(target=worker)
16thread.start()
17
18for n in range(5):
19    q.put(n)
20
21q.join()
22q.put(None)
23thread.join()

This removes shared-state lock contention entirely for serialized operations.

Timeout and Cancellation Design

If fairness is required, also define timeout behavior. A thread blocked in the queue should be able to abort cleanly in real services. Extending FIFO lock with timeouts requires careful queue-token removal to avoid dead waiters.

Multiprocessing Note

threading locks work only for threads in one process. For multiprocessing fairness, use process-safe primitives from multiprocessing and design queue semantics at process level.

Observability for Contention

If fairness is critical, add contention metrics such as average wait time, maximum wait time, and queue length over time. These measurements reveal starvation risks before users notice latency spikes. Logging lock acquisition and release timestamps at debug level for controlled load tests can quickly show whether lock fairness or workload partitioning needs adjustment.

Choosing Fairness Tradeoffs

Fair lock queues improve predictability but can lower peak throughput in some workloads. Evaluate whether fairness is a strict requirement or whether reducing critical section duration gives better results with simpler primitives.

Common Pitfalls

  • Assuming threading.Lock is fair by default
  • Building queue locks without handling owner validation
  • Forgetting to notify waiters on release
  • Ignoring timeout and cancellation requirements in production
  • Using lock fairness where a work queue architecture is simpler

Queueing locks can help, but a queue-based execution model is often easier to reason about.

Summary

  • Standard Python locks provide exclusion, not fairness guarantees.
  • FIFO lock patterns can enforce queue order across waiting threads.
  • Work queues are often a cleaner alternative to fair locking.
  • Add timeout and cancellation semantics for robust services.
  • Choose the concurrency primitive that matches workload behavior.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.