Какие механизмы предоставляет модуль multiprocessing в Python для обмена данными между процессами?

«Какие механизмы предоставляет модуль multiprocessing в Python для обмена данными между процессами?» — вопрос из категории Асинхронность, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Модуль multiprocessing в Python предлагает несколько механизмов для безопасного обмена данными между процессами (IPC - Inter-Process Communication), так как процессы по умолчанию не разделяют память и работают в изолированных адресных пространствах.

  1. Очереди (multiprocessing.Queue)

    • Описание: Потокобезопасная очередь, работающая по принципу FIFO (First-In, First-Out). Объекты перед помещением в очередь сериализуются (pickle).
    • Сценарий: Идеально подходит для организации обмена сообщениями между несколькими процессами-производителями (producers) и процессами-потребителями (consumers).
      
      from multiprocessing import Process, Queue

    def worker(q): q.put('Hello from child process')

    if name == 'main': q = Queue() p = Process(target=worker, args=(q,)) p.start() print(q.get()) # Вывод: Hello from child process p.join()

  2. Каналы (multiprocessing.Pipe)

    • Описание: Создает пару соединенных объектов Connection, представляющих два конца канала. По умолчанию канал является двунаправленным.
    • Сценарий: Эффективен для двусторонней связи между двумя конкретными процессами.
      
      from multiprocessing import Process, Pipe

    def worker(conn): conn.send([42, None, 'hello']) conn.close()

    if name == 'main': parent_conn, child_conn = Pipe() p = Process(target=worker, args=(child_conn,)) p.start() print(parent_conn.recv()) # Вывод: [42, None, 'hello'] p.join()

  3. Разделяемая память (Value и Array)

    • Описание: Позволяет нескольким процессам совместно использовать и изменять одни и те же данные в памяти. Value для одного значения, Array для массива простых типов (числа, символы).
    • Сценарий: Самый быстрый способ обмена, так как избегает накладных расходов на сериализацию. Требует использования блокировок (Lock) для предотвращения состояния гонки при одновременной записи.
      
      from multiprocessing import Process, Value, Array, Lock

    def worker(n, a, lock): with lock: n.value += 1 for i in range(len(a)): a[i] = -a[i]

    if name == 'main': lock = Lock() num = Value('d', 0.0) arr = Array('i', range(10)) p = Process(target=worker, args=(num, arr, lock)) p.start() p.join() print(num.value) # Вывод: 1.0 print(list(arr)) # Вывод: [0, -1, -2, ...]

Механизм Основной сценарий Производительность Сложность использования
Queue Обмен сообщениями (многие-ко-многим) Средняя Низкая
Pipe Двусторонняя связь (один-к-одному) Высокая Низкая
Value/Array Общий доступ к простым данным Очень высокая Средняя (нужны блокировки)