Как синхронизировать процессы с помощью `multiprocessing.Lock()`?

Используйте `multiprocessing.Lock()` для создания объекта блокировки. Перед критической секцией кода вызывайте метод `lock.acquire()` для получения блокировки. После выполнения критической секции вызывайте `lock.release()` для освобождения блокировки. Это гарантирует, что только один процесс одновременно выполняет критическую секцию, предотвращая состояние гонки и повреждение данных. Можно использовать `with lock:` для автоматического приобретения и освобождения блокировки.

Синхронизация процессов с помощью multiprocessing.Lock() позволяет предотвратить состояние гонки (race condition) и обеспечить безопасный доступ к общим ресурсам (например, файлам, переменным) из нескольких процессов. Lock предоставляет базовый механизм взаимоисключения, гарантируя, что только один процесс в данный момент времени может владеть этим блокировщиком.

Вот как это работает:

  1. Создание Lock: Сначала создается экземпляр объекта Lock. Это делается обычно в главной программе, до запуска дочерних процессов.
  2. Приобретение Lock: В каждом процессе, который должен получить доступ к общему ресурсу, вызывается метод lock.acquire(). Если блокировщик свободен, процесс захватывает его и продолжает выполнение. Если блокировщик уже занят другим процессом, вызывающий процесс блокируется до тех пор, пока блокировщик не будет освобожден.
  3. Безопасный доступ к ресурсу: После того, как процесс захватил блокировщик, он может безопасно работать с общим ресурсом.
  4. Освобождение Lock: После завершения работы с общим ресурсом, процесс должен освободить блокировщик, вызвав метод 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} завершил работу.")
0