2026, Jan 14 06:02
acquire_lock и release_lock в threading.Lock: почему это алиасы и что использовать
Python 3.13: недокументированные алиасы threading.Lock — acquire_lock и release_lock. Почему их не стоит вызывать и как безопасно работать с блокировками.
Недокументированные двойники в Lock Python: что на самом деле представляют собой acquire_lock и release_lock
Работа с threading.Lock в Python 3.13 может сбивать с толку, когда IDE показывают методы вроде acquire_lock, release_lock и даже locked_lock рядом с публичными acquire, release и locked. Возникает естественный вопрос: это разные ветки кода или просто альтернативные имена, и какие из них стоит вызывать в рабочем коде.
Код, из-за которого возникает вопрос
Допустим, вы видите или пишете код, который использует имена с суффиксом «_lock»:
import threading
mutex_gate = threading.Lock()
mutex_gate.acquire_lock()
try:
# критическая секция, защищённая блокировкой
payload = "work"
finally:
mutex_gate.release_lock()
Это выполняется, но заставляет насторожиться: таких методов нет в публичной документации. Есть ли у них какие‑то особенности по сравнению с acquire и release?
Что происходит на самом деле
Это просто алиасы. В CPython acquire_lock указывает на ту же базовую функцию, что и acquire, а release_lock — на ту же реализацию, что и release. Судя по истории проекта, так уже около 15 лет. Имена существуют, но намеренно не документируются, то есть не входят в публичный контракт API и могут быть удалены в любой момент. В отличие от этого, acquire() документирован, а acquire_lock() — нет; уже один этот факт ясно показывает, какой метод ожидается использовать в реальном коде.
Некоторые IDE могут показывать дополнительные имена в сигнатурах или подсказках, что вводит в заблуждение. Также обратите внимание: в публичной части threading.py Lock представлен иначе; варианты с суффиксом «lock» — это внутренние алиасы, а не пользовательское API.
Как правильно писать код
Самый безопасный и удобный для сопровождения шаблон — использовать менеджер контекста. Он захватывает блокировку ровно в месте начала блока with и освобождает её сразу при выходе из блока, в том числе при исключениях. Никаких временных накладных расходов по сравнению с ручными вызовами; вы чётко ограничиваете строки, которые должны быть защищены.
import threading
sync_guard = threading.Lock()
with sync_guard:
# защищаемый участок
result = "work"
Если вы предпочитаете явные вызовы acquire/release, используйте только документированные имена и обязательно сочетайте их с try/finally, чтобы блокировка всегда освобождалась:
import threading
spin_lock = threading.Lock()
spin_lock.acquire()
try:
# защищаемый участок
outcome = "work"
finally:
spin_lock.release()
Функционально этот вариант и форма с with эквивалентны по области действия и по времени. Просто with сложнее использовать неверно.
Почему это важно знать
Недокументированные API хрупки. Даже если сегодня всё работает, у них нет гарантий стабильности — они могут исчезнуть или измениться без предупреждения. Использование публичных методов acquire, release и locked — или ещё лучше, протокола менеджера контекста — держит ваш код в рамках официального контракта и избавляет от сюрпризов между версиями Python и окружениями.
Практический вывод
Если в инструментах попадаются acquire_lock, release_lock или locked_lock, считайте их внутренними алиасами и избегайте их. Предпочитайте with lock: ради ясности и безопасности при исключениях. Если нужен ручной контроль, вызывайте acquire() и release() внутри try/finally. Эта небольшая дисциплина делает конкурентный код предсказуемым и устойчивым, оставаясь в русле поддерживаемой документацией.