threading.Lock()
для создания объекта блокировки. Перед критической секцией кода, вызывайте метод lock.acquire()
, чтобы захватить блокировку. После выполнения критической секции, вызывайте lock.release()
, чтобы освободить блокировку. Только один поток может владеть блокировкой в один момент времени, что предотвращает гонки данных и обеспечивает синхронизацию. Рекомендуется использовать конструкцию try...finally
или with lock:
чтобы гарантировать освобождение блокировки даже при возникновении исключений.
Синхронизация потоков с помощью threading.Lock()
в Python позволяет избежать состояния гонки (race condition) и обеспечить корректную работу при обращении нескольких потоков к общим ресурсам. threading.Lock()
реализует механизм мьютекса (mutual exclusion), предоставляя только одному потоку доступ к защищенному коду в любой момент времени.
Основной принцип использования заключается в следующем:
threading.Lock()
, который будет использоваться для защиты критической секции кода.lock.acquire()
. Если блокировка свободна, поток захватывает ее и продолжает выполнение. Если блокировка уже захвачена другим потоком, текущий поток будет заблокирован (ожидать), пока блокировка не будет освобождена.lock.release()
, чтобы освободить блокировку. Это позволяет другим потокам, ожидающим блокировку, продолжить выполнение.Пример кода:
import threading
import time
# Общий ресурс (например, счетчик)
shared_counter = 0
# Создаем блокировку
lock = threading.Lock()
def increment_counter(thread_id):
global shared_counter
for _ in range(100000):
# Захватываем блокировку
lock.acquire()
try:
shared_counter += 1
# Критическая секция: изменяем общий ресурс
finally:
# Освобождаем блокировку (важно делать в блоке finally, чтобы гарантировать освобождение даже при исключениях)
lock.release()
print(f"Поток {thread_id} завершил работу")
# Создаем и запускаем потоки
threads = []
for i in range(2):
thread = threading.Thread(target=increment_counter, args=(i,))
threads.append(thread)
thread.start()
# Ожидаем завершения всех потоков
for thread in threads:
thread.join()
print(f"Финальное значение счетчика: {shared_counter}")
Важные моменты:
try...finally
для освобождения блокировки, чтобы гарантировать ее освобождение даже в случае возникновения исключений внутри критической секции. Не освобождение блокировки может привести к дедлоку (deadlock).threading.Lock()
является реентерабельным (reentrant). Это означает, что поток, уже владеющий блокировкой, может захватить ее повторно без блокировки, но он также должен освободить ее столько же раз, сколько раз он ее захватил. Для реентерабельных блокировок используйте threading.RLock()
.