Python's `with lock:` Guarantees Release on Exception
Python's `with lock:` is sugar for acquire plus try/finally release, guaranteeing the lock frees itself even when the critical section throws an exception.
Python Under Multithreading
with lock: Guarantees Release Even on Exception
Manual acquire()/release() without try/finally means: if the body throws, the lock never releases — every subsequent caller trying to acquire it gets stuck, turning into a permanent deadlock.
with lock: is really syntactic sugar for acquire + try/finally release:
with self._login_lock: # lock.acquire()
... # try: ... finally: lock.release()
release() is guaranteed to run when the block ends — even if the body throws an exception.
Real-world case: the critical section of login() calls session.post() and raise_for_status(), both of which can throw. Using with self._login_lock: ensures the lock releases on a network error, preventing one failed login from blocking every subsequent login on that worker.
Every synchronization primitive in Python’s threading library supports with: Lock, RLock, Condition, Semaphore, BoundedSemaphore. Use with whenever possible.
Releasing a lock shouldn’t depend on the happy path. Use
with, and hand it off tofinally.
References:
- https://docs.python.org/3/library/threading.html
- https://superfastpython.com/thread-context-manager/
Related: see the double-checked locking breakdown of two checks, two jobs, the finish-first, then-publish safe publication pattern, or go back to the four-lesson overview on making login thread-safe.