Process vs. Thread with regards to using Queue()/deque() and class variable for communication and “poison pill”

后端 未结 1 1315
既然无缘
既然无缘 2020-12-18 13:16

I would like to create either a Thread or a Process which runs forever in a While True loop.

I need to send and receive data to the worker in the form for queues, ei

相关标签:
1条回答
  • 2020-12-18 13:40

    You can't use a collections.deque to pass data between two multiprocessing.Process instances, because collections.deque is not process-aware. multiprocessing.Queue writes its contents to a multiprocessing.Pipe internally, which means that data in it can be enqueued in once process and retrieved in another. collections.deque doesn't have that kind of plumbing, so it won't work. When you write to the deque in one process, the deque instance in the other process won't be affected at all; they're completely separate instances.

    A similar issue is happening to your stop() method. You're changing the value of toRun in the main process, but this won't affect the child at all. They're completely separate instances. The best way to end the child would be to send some sentinel to the Queue. When you get the sentinel in the child, break out of the infinite loop:

    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")
    

    Edit:

    If you actually do need deque semantics between two process, you can use a custom multiprocessing.Manager() to create a shared deque in a Manager process, and each of your Process instances will get a Proxy to it:

    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)
    

    Note that this probably isn't going to be faster than a multiprocessing.Queue, though, since there's an IPC cost for every time you access the deque. It's also a much less natural data structure for passing messages the way you are.

    0 讨论(0)
提交回复
热议问题