← Назад к вопросам

Можно ли одну и ту же переменную использовать в разных процессах в многопроцессинге в Python?

2.0 Middle🔥 131 комментариев
#Python Core#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Можно ли одну и ту же переменную использовать в разных процессах

Нет, в обычном многопроцессинге Python переменные не общаются напрямую между процессами. Каждый процесс имеет собственное пространство памяти. Однако есть специальные механизмы для совместного доступа к данным.

Проблема: Каждый процесс имеет копию данных

from multiprocessing import Process

shared_list = [1, 2, 3]

def worker():
    shared_list.append(4)
    print(f"Worker: {shared_list}")  # [1, 2, 3, 4]

if __name__ == "__main__":
    process = Process(target=worker)
    process.start()
    process.join()
    
    print(f"Main: {shared_list}")  # [1, 2, 3] - НЕ изменился!

При создании процесса Python копирует всё содержимое памяти (forking).

Решение 1: multiprocessing.Queue

Queue позволяет безопасно передавать данные между процессами:

from multiprocessing import Process, Queue

def worker(queue):
    queue.put("Data from worker")
    queue.put(42)

if __name__ == "__main__":
    q = Queue()
    process = Process(target=worker, args=(q,))
    process.start()
    
    message = q.get()  # "Data from worker"
    number = q.get()   # 42
    
    process.join()
    print(f"Received: {message}, {number}")

Решение 2: multiprocessing.Pipe

Pipe работает как двусторонний канал:

from multiprocessing import Process, Pipe

def worker(conn):
    conn.send("Hello from worker")
    msg = conn.recv()
    print(f"Worker received: {msg}")
    conn.close()

if __name__ == "__main__":
    parent_conn, child_conn = Pipe()
    
    process = Process(target=worker, args=(child_conn,))
    process.start()
    
    msg = parent_conn.recv()
    print(f"Parent received: {msg}")
    parent_conn.send("Hello from parent")
    
    process.join()

Решение 3: multiprocessing.Value и Array

Это истинная общая память между процессами:

from multiprocessing import Process, Value, Array

def worker(shared_int, shared_array):
    shared_int.value = 100
    shared_array[0] = 999
    shared_array[1] = 888

if __name__ == "__main__":
    shared_int = Value("i", 0)  # 'i' = integer
    shared_array = Array("i", [1, 2, 3])
    
    process = Process(target=worker, args=(shared_int, shared_array))
    process.start()
    process.join()
    
    print(f"shared_int = {shared_int.value}")        # 100
    print(f"shared_array = {list(shared_array)}")    # [999, 888, 3]

Поддерживаемые типы:

  • 'i' — int
  • 'd' — double
  • 'f' — float
  • 'b' — byte
  • 'c' — char

Решение 4: multiprocessing.Manager

Manager позволяет использовать сложные типы данных:

from multiprocessing import Process, Manager

def worker(shared_dict, shared_list):
    shared_dict["user"] = "Alice"
    shared_dict["age"] = 30
    shared_list.append("worker_item")

if __name__ == "__main__":
    with Manager() as manager:
        shared_dict = manager.dict()
        shared_list = manager.list()
        
        process = Process(target=worker, args=(shared_dict, shared_list))
        process.start()
        process.join()
        
        print(f"Dict: {dict(shared_dict)}")     # {'user': 'Alice', 'age': 30}
        print(f"List: {list(shared_list)}")     # ['worker_item']

Поддерживаемые типы:

  • manager.dict() — словарь
  • manager.list() — список
  • manager.set() — множество
  • manager.Queue() — очередь

Решение 5: Lock (Mutex) для безопасного доступа

При общей памяти нужны блокировки для избежания race conditions:

from multiprocessing import Process, Value, Lock

def worker(counter, lock):
    for _ in range(100000):
        with lock:  # Захватываем блокировку
            counter.value += 1

if __name__ == "__main__":
    counter = Value("i", 0)
    lock = Lock()
    
    processes = [
        Process(target=worker, args=(counter, lock))
        for _ in range(4)
    ]
    
    for p in processes:
        p.start()
    
    for p in processes:
        p.join()
    
    print(f"Counter: {counter.value}")  # 400000 (правильно с lock)

Без Lock результат неправильный:

# Без lock: counter.value может быть < 400000
# С lock: counter.value = 400000

Решение 6: RawValue и RawArray

Если не нужна синхронизация:

from multiprocessing import Process, RawValue, RawArray

def worker(value, array):
    value.value = 50
    array[0] = 999

if __name__ == "__main__":
    value = RawValue("i", 0)
    array = RawArray("i", [1, 2, 3])
    
    process = Process(target=worker, args=(value, array))
    process.start()
    process.join()
    
    print(f"Value: {value.value}")
    print(f"Array: {list(array)}")

Сравнение подходов

МетодСкоростьСложностьИспользование
QueueМедленноПростаяАсинхронная передача
PipeБыстроПростаяПарные процессы
Value/ArrayОчень быстроСредняяПростые типы + Lock
ManagerМедленноСложнаяСложные типы
RawValue/RawArrayСамая быстроСредняяБез синхронизации

Практический пример: Worker Pool

from multiprocessing import Process, Queue, Manager

def worker(input_queue, output_dict, worker_id):
    while True:
        task = input_queue.get()
        if task is None:  # Сигнал завершения
            break
        
        result = task ** 2
        output_dict[task] = result
        print(f"Worker {worker_id}: {task}² = {result}")

if __name__ == "__main__":
    input_queue = Queue()
    
    with Manager() as manager:
        output_dict = manager.dict()
        
        workers = [
            Process(target=worker, args=(input_queue, output_dict, i))
            for i in range(4)
        ]
        
        for p in workers:
            p.start()
        
        for num in range(1, 11):
            input_queue.put(num)
        
        for _ in range(4):
            input_queue.put(None)
        
        for p in workers:
            p.join()
        
        print(f"Results: {dict(output_dict)}")

Когда использовать что

Queue/Pipe:

  • Асинхронная коммуникация
  • Разные типы данных
  • Масштабируемость

Value/Array + Lock:

  • Высокая скорость
  • Простые типы
  • Частые обновления

Manager:

  • Сложные типы (dict, list, set)
  • Простота использования

Заключение

В многопроцессинге переменные НЕ общаются напрямую. Используй:

  1. Queue — для асинхронной передачи
  2. Pipe — для синхронной коммуникации
  3. Value/Array + Lock — для быстрого доступа
  4. Manager — для сложных структур