Python:进程中线程中队列处理的奇怪之处

时间:2017-09-18 14:14:02

标签: python multithreading

我有一个奇怪的使用python进行队列处理的情况。让我先描述一下:

  1. 主线程创建一个全局对象,负责收集和处理消息(对象内部有队列)
  2. 该全局对象具有在线程中启动的方法。方法可以访问队列(属于对象)并从中读取消息
  3. 主线程启动一系列进程(使用multiprocessing.Process),将消息发布到全局对象。
  4. 问题是:对于队列处理器,队列始终为空。让我用一些代码说明问题:

    import threading as t
    import time
    import sys
    from multiprocessing import Process
    if sys.version_info.major is 2:
        import Queue as queue
    else:
        import queue
    
    class ExampleRecorder:
        queue = queue.Queue()
    
        def run(self):
            self.thread = t.Thread(target=self.start_processor)
            self.thread.daemon = True
            self.thread.start()
    
        def start_processor(self):
            while 1:
                print("PROCESSOR! QUEUE ID: {}. QUEUE SIZE: {}. IS EMPTY: {}".format(id(self.queue), self.queue.qsize(), self.queue.empty()))
                time.sleep(1)
    
        def push_message(self, span):
            self.queue.put(span)
            print("RECORDER! QUEUE ID: {}. QUEUE SIZE: {}".format(id(self.queue), self.queue.qsize()))
    
    er = ExampleRecorder()
    er.run()
    
    def producer():
        while 1:
            print("Adding an item")
            er.push_message("foo")
            time.sleep(1)
    
    
    proc = Process(target=producer)
    proc.start()
    

    脚本的示例输出为:

    $ python3 model.py
    PROCESSOR! QUEUE ID: <queue.Queue object at 0x1095f9b70>. QUEUE SIZE: 0. IS EMPTY: True
    Adding an item
    RECORDER! QUEUE ID: <queue.Queue object at 0x1095f9b70>. QUEUE SIZE: 1
    PROCESSOR! QUEUE ID: <queue.Queue object at 0x1095f9b70>. QUEUE SIZE: 0. IS EMPTY: True
    Adding an item
    RECORDER! QUEUE ID: <queue.Queue object at 0x1095f9b70>. QUEUE SIZE: 2
    PROCESSOR! QUEUE ID: <queue.Queue object at 0x1095f9b70>. QUEUE SIZE: 0. IS EMPTY: True
    

    如您所见,队列接收对象并增长,但对于处理器,它始终为空。那里可能出现什么问题?在一个进程中的线程中处理共享队列是否有些棘手?

    P.S。检查Python 2.7.12和3.5.2

1 个答案:

答案 0 :(得分:2)

Queue.Queue仅适用于同一个流程,它与线程一起使用,而不是与单独的流程一起使用。

您需要一个multiprocessing.Queue实例来进行进程间通信,但您还必须重新构建代码以将队列实例明确传递给producer 。就像现在一样,在新进程中评估ExampleRecorder定义时,每个生成器都会创建一个不同的实例。

注意:使用您编写的代码,所有ExampleRecorder个实例(同一进程中的 )共享同一个队列!你确定这是你想要的吗?

通过在queue块中定义class ...queue类的属性,而不是其实例。这与在queue中定义__init__()非常不同。用一个更简单的例子:

from queue import Queue

class SampleClass:
  queue = Queue()

class AnotherSample:
  def __init__(self):
    self.queue = Queue()

inst1 = SampleClass()
inst2 = SampleClass()

inst3 = AnotherSample()
inst4 = AnotherSample()

如果我们测试queue属性,我们会看到差异:( is运算符测试两个变量是否是同一对象的别名)

inst1.queue is inst2.queue
Out[8]: True

inst3.queue is inst4.queue
Out[9]: False