Синхронизация процессов с помощью multiprocessing.Lock()
позволяет предотвратить состояние гонки (race condition) и обеспечить безопасный доступ к общим ресурсам (например, файлам, переменным) из нескольких процессов. Lock
предоставляет базовый механизм взаимоисключения, гарантируя, что только один процесс в данный момент времени может владеть этим блокировщиком.
Вот как это работает:
Lock
. Это делается обычно в главной программе, до запуска дочерних процессов.lock.acquire()
. Если блокировщик свободен, процесс захватывает его и продолжает выполнение. Если блокировщик уже занят другим процессом, вызывающий процесс блокируется до тех пор, пока блокировщик не будет освобожден.lock.release()
. Это позволяет другому ожидающему процессу захватить блокировщик и получить доступ к ресурсу.Рекомендуется использовать блокировку в блоке try...finally
, чтобы гарантировать, что блокировщик будет освобожден даже в случае возникновения исключения.
Пример:
import multiprocessing
import time
def worker(lock, shared_resource):
lock.acquire() # Захватываем блокировщик
try:
print(f"Процесс {multiprocessing.current_process().name} начал работу.")
# Критическая секция - безопасный доступ к общему ресурсу
shared_resource.value += 1
time.sleep(1) # Имитация работы с ресурсом
print(f"Процесс {multiprocessing.current_process().name} увеличил shared_resource до {shared_resource.value}")
finally:
lock.release() # Освобождаем блокировщик (обязательно!)
print(f"Процесс {multiprocessing.current_process().name} завершил работу.")
if __name__ == '__main__':
lock = multiprocessing.Lock()
shared_resource = multiprocessing.Value('i', 0) # 'i' означает integer
processes = []
for i in range(3):
p = multiprocessing.Process(target=worker, args=(lock, shared_resource), name=f"Process-{i+1}")
processes.append(p)
p.start()
for p in processes:
p.join()
print(f"Финальное значение shared_resource: {shared_resource.value}")
В этом примере, shared_resource
- это общая переменная, а lock
- это блокировщик. Каждый процесс пытается увеличить значение shared_resource
на 1. Без блокировки, несколько процессов могли бы одновременно прочитать значение shared_resource
, увеличить его и записать обратно, что привело бы к неправильному конечному значению. Блокировщик гарантирует, что только один процесс одновременно имеет доступ к критической секции кода, где изменяется shared_resource
, таким образом избегая состояния гонки.
Альтернативой использованию lock.acquire()
и lock.release()
является использование контекстного менеджера with lock:
, который автоматически захватывает и освобождает блокировщик, что делает код чище и предотвращает забывание освобождения блокировщика:
def worker_with_context_manager(lock, shared_resource):
with lock: # Автоматически захватывает и освобождает блокировщик
print(f"Процесс {multiprocessing.current_process().name} начал работу.")
shared_resource.value += 1
time.sleep(1)
print(f"Процесс {multiprocessing.current_process().name} увеличил shared_resource до {shared_resource.value}")
print(f"Процесс {multiprocessing.current_process().name} завершил работу.")