Процесс против потока в отношении использования Queue()/deque() и переменной класса для связи и "ядовитой таблетки"

87
9

Я хотел бы создать либо Thread, либо Process, который выполняется навсегда в цикле While True.

Мне нужно отправить и получить данные работнику в форме для очередей, либо многопроцессорное.Queue(), либо collection.deque(). Я предпочитаю использовать collections.deque(), поскольку он значительно быстрее.

Я также должен иметь возможность убить работника в конце концов (поскольку он работает в то время как True loop. Вот несколько тестовых кодов, которые я собрал, чтобы попытаться понять различия между Threads, Processes, Queues и deque..

import time
from multiprocessing import Process, Queue
from threading import Thread
from collections import deque

class ThreadingTest(Thread):

def __init__(self, q):
super(ThreadingTest, self).__init__()
self.q = q
self.toRun = False

def run(self):
print("Started Thread")
self.toRun = True
while self.toRun:
if type(self.q) == type(deque()):
if self.q:
i = self.q.popleft()
print("Thread deque: " + str(i))
elif type(self.q) == type(Queue()):
if not self.q.empty():
i = self.q.get_nowait()
print("Thread Queue: " + str(i))

def stop(self):
print("Trying to stop Thread")
self.toRun = False
while self.isAlive():
time.sleep(0.1)
print("Stopped Thread")

class ProcessTest(Process):

def __init__(self, q):
super(ProcessTest, self).__init__()
self.q = q
self.toRun = False
self.ctr = 0

def run(self):
print("Started Process")
self.toRun = True
while self.toRun:
if type(self.q) == type(deque()):
if self.q:
i = self.q.popleft()
print("Process deque: " + str(i))
elif type(self.q) == type(Queue()):
if not self.q.empty():
i = self.q.get_nowait()
print("Process Queue: " + str(i))

def stop(self):
print("Trying to stop Process")
self.toRun = False
while self.is_alive():
time.sleep(0.1)
print("Stopped Process")

if __name__ == '__main__':
q = Queue()
t1 = ProcessTest(q)
t1.start()

for i in range(10):
if type(q) == type(deque()):
q.append(i)
elif type(q) == type(Queue()):
q.put_nowait(i)
time.sleep(1)
t1.stop()
t1.join()

if type(q) == type(deque()):
print(q)
elif type(q) == type(Queue()):
while q.qsize() > 0:
print(str(q.get_nowait()))

Как вы можете видеть, t1 может быть либо ThreadingTest, либо ProcessTest. Кроме того, переданная ему очередь может быть либо многопроцессорной.Queue, либо collection.deque.

ThreadingTest работает с очередью или deque(). Он также убивает run() правильно, когда вызывается метод stop().

Started Thread
Thread deque: 0
Thread deque: 1
Thread deque: 2
Thread deque: 3
Thread deque: 4
Thread deque: 5
Thread deque: 6
Thread deque: 7
Thread deque: 8
Thread deque: 9
Trying to stop Thread
Stopped Thread
deque([])

ProcessTest может читать только из очереди, если он имеет тип multiprocessing.Queue. Он не работает с collection.deque. Кроме того, я не могу убить процесс, используя stop().

Process Queue: 0
Process Queue: 1
Process Queue: 2
Process Queue: 3
Process Queue: 4
Process Queue: 5
Process Queue: 6
Process Queue: 7
Process Queue: 8
Process Queue: 9
Trying to stop Process

Я пытаюсь понять, почему? Кроме того, что было бы лучшим способом использовать deque с процессом? И как я могу убить процесс, используя какой-то метод stop().

спросил(а) 2021-01-19T18:13:00+03:00 2 месяца, 3 недели назад
1
Решение
122

Вы не можете использовать файл collections.deque для передачи данных между двумя экземплярами multiprocessing.Process, поскольку collections.deque не поддерживает процесс. multiprocessing.Queue записывает свое содержимое в multiprocessing.Pipe внутренне, что означает, что данные в нем могут быть очереди в один процесс и получить в другом. collections.deque не имеет такого водопровода, так что это не сработает. Когда вы пишете на deque в одном процессе, экземпляр deque в другом процессе вообще не будет затронут; они полностью отдельные экземпляры.

Аналогичная проблема происходит с вашим методом stop(). Вы меняете значение toRun в основном процессе, но это никак не повлияет на ребенка. Это совершенно разные экземпляры. Лучший способ положить конец ребенку - это послать какого-то стражника в Queue. Когда вы получите часового в ребенке, вырвайтесь из бесконечного цикла:

def run(self):
print("Started Process")
self.toRun = True
while self.toRun:
if type(self.q) == type(deque()):
if self.q:
i = self.q.popleft()
print("Process deque: " + str(i))
elif type(self.q) == type(Queue()):
if not self.q.empty():
i = self.q.get_nowait()
if i is None:
break # Got sentinel, so break
print("Process Queue: " + str(i))

def stop(self):
print("Trying to stop Process")
self.q.put(None) # Send sentinel
while self.is_alive():
time.sleep(0.1)
print("Stopped Process")

Редактировать:

Если вам действительно нужна семантика deque между двумя процессами, вы можете использовать пользовательский multiprocessing.Manager() для создания общего deque в процессе Manager, и каждый из экземпляров Process получит Proxy:

import time
from multiprocessing import Process
from multiprocessing.managers import SyncManager
from collections import deque

SyncManager.register('deque', deque)

def Manager():
m = SyncManager()
m.start()
return m

class ProcessTest(Process):
def __init__(self, q):
super(ProcessTest, self).__init__()
self.q = q
self.ctr = 0

def run(self):
print("Started Process")
self.toRun = True
while self.toRun:
if self.q._getvalue():
i = self.q.popleft()
if i is None:
break
print("Process deque: " + str(i))

def stop(self):
print("Trying to stop Process")
self.q.append(None)
while self.is_alive():
time.sleep(0.1)
print("Stopped Process")

if __name__ == '__main__':
m = Manager()
q = m.deque()
t1 = ProcessTest(q)
t1.start()

for i in range(10):
q.append(i)
time.sleep(1)
t1.stop()
t1.join()

print(q)

Обратите внимание, что это, вероятно, не будет быстрее, чем multiprocessing.Queue Однако, поскольку стоимость IPC при каждом доступе к deque. Это также гораздо менее естественная структура данных для передачи сообщений так, как вы.

ответил(а) 2021-01-19T18:13:00+03:00 2 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема