Python multiprocessing, не передается значение переменной из одной функции в другую

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

Итак, есть 3 функции: stream, print_sound - прослушка микрофона и детектор громкости, если громкость есть то она суммируется и one которая печатает все время сумму громкости, вот только она не печатает, вывод идет все время 0, хотя если принт написать в print_sound, то все нормально суммируется и выводится.

Подскажите, почему так, и как пофиксить?

PS: изучаю python месяц

Код ниже:

import sounddevice as sd
import numpy as np
import keyboard
import time as t
import pyautogui
import multiprocessing

r=0
rr = 0

def one():
    try:
        while True:
            print(rr)

    except KeyboardInterrupt:
        print('1')

def stream():
    global st
    with sd.Stream(callback=print_sound):
        sd.sleep(1000000)

def print_sound(indata, outdata, frames, time, status):
    global rr
    volume_norm = np.linalg.norm(indata)*10
    r = int(volume_norm)
    rr = rr+r

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=stream)
    p3 = multiprocessing.Process(target=one)
    p1.start()
    p3.start()
    p1.join()
    p3.join()

Ответы (1 шт):

Автор решения: Roman Konoval

У каждого процесса - своя память. Это означает, что у каждого процесса своя копия переменной rr. Когда один процесс ее модифицирует (у себя в памяти), другой этого не видит, он видит то значения, которое было у переменной, в момент создания процесса (когда память скопировалась из родительского процесса).

Для того, чтоб можно было обмениваться данными между процессами, нужно использовать специальные механизмы межпроцессного взаимодействия, представленные утилитами из модуля multiprocessing.

Managers

Менеджеры позволяют создавать переменные, которые разделяются между процессами. Поддерживаются:

  • просто значение - Manager.Value
  • словарь Manager.dict
  • список Manager.list
  • и др.

Если говорить грубо и упрощенно, то при изменении переменной менеджер заботится о том, чтоб значение синхронизировалось с другими процессами.

Пример использования Manager.Value:

from time import sleep
import multiprocessing

def watcher(var):
    while True:
        print(f'watcher: {var.value}')
        sleep(0.3)

def modifier(var):
    for i in range(5):
        var.value += 1
        print(f'modifier: modified {var.value}')
        sleep(1)
    print(f'modifier: done')

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    var = manager.Value('var', 0)
    p1 = multiprocessing.Process(target=modifier, args=(var,))
    p2 = multiprocessing.Process(target=watcher, daemon=True, args=(var,))
    p1.start()
    p2.start()
    p1.join()

Очереди и каналы

Позволяют посылать однонаправленный (Queue) и двунаправленные (Pipe) потоки данных между процессами.

Пример использования multiprocessing.Queue:

from time import sleep
from multiprocessing import Process, Queue

def producer(queue):
    for i in range(1, 5):
        queue.put(i)
        print(f"producer: put {i}")
        sleep(1)
    print("producer: done")

def consumer(queue):
    while True:
        item = queue.get()
        if item is None:
            break
        print(f"consumer: get {item}")
    print("consumer: done")

if __name__ == '__main__':
    queue = Queue()
    p1 = Process(target=producer, args=(queue,))
    p2 = Process(target=consumer, daemon=True, args=(queue,))
    p1.start()
    p2.start()
    p1.join()
    print("main: done")

Пример использования multiprocess.Pipe:

from time import sleep
from multiprocessing import Process, Pipe

def ping_pong(name, pipe, initiate):
    if initiate:
        print(f'{name}: sent 1')
        pipe.send(1)
    while True:
        val = pipe.recv()
        print(f'{name}: received {val}')
        if val > 5:
            break
        val += 1
        sleep(1)
        pipe.send(val)
        print(f'{name}: sent {val}')
    print(f"{name}: done")

if __name__ == '__main__':
    end1, end2 = Pipe()
    p1 = Process(target=ping_pong, daemon=True, args=('ping', end1, True))
    p2 = Process(target=ping_pong, daemon=True, args=('pong', end2, False))
    p1.start()
    p2.start()
    while p1.is_alive() and p2.is_alive():
        sleep(1)
    print("main: done")

Решение для задачи из вопроса

Для решение задачи из вопроса удобно поспользоваться Manager.Value:

import multiprocessing
from functools import partial

def one(rr):
    while True:
        print(rr.value)

def stream(rr):
    global st
    with sd.Stream(callback=partial(print_sound, rr)):
        sd.sleep(1000000)

def print_sound(rr, indata, outdata, frames, time, status):
    volume_norm = np.linalg.norm(indata)*10
    r = int(volume_norm)
    rr.value += r

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    rr = manager.Value('rr', 0)
    p1 = multiprocessing.Process(target=stream, args=(rr,))
    p3 = multiprocessing.Process(target=one, args=(rr,))
    # ... тут все как раньше
→ Ссылка