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 шт):
У каждого процесса - своя память. Это означает, что у каждого процесса своя копия переменной 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,))
# ... тут все как раньше