在多个Python多处理队列上“选择”?

时间:2009-07-14 06:58:17

标签: python events select synchronization multiprocessing

在两个(多处理)Queues中的任何一个可用的情况下等待(不旋转)的最佳方式是什么,两者都位于同一系统中?

9 个答案:

答案 0 :(得分:25)

实际上你可以在select.select中使用multiprocessing.Queue对象。即

que = multiprocessing.Queue()
(input,[],[]) = select.select([que._reader],[],[])

只有在准备好读取时才会选择que。

虽然没有关于它的文档。我正在阅读multiprocessing.queue库的源代码(在linux上它通常像/usr/lib/python2.6/multiprocessing/queue.py一样)来查找它。

使用Queue.Queue我没有找到任何聪明的方法来做到这一点(我真的很喜欢)。

答案 1 :(得分:14)

看起来还没有正式的方法来处理这个问题。或者至少,不是基于此:

您可以尝试类似这篇文章的内容 - 访问底层管道文件句柄:

然后使用select。

答案 2 :(得分:2)

似乎使用线程将传入的项目转发到单个队列,然后在以独立于平台的方式使用多处理时,等待它是一个实用的选择。

避免线程需要处理低级管道/ FD,这些管道/ FD既是平台特定的,也不容易与更高级别的API一致处理。

或者你需要能够设置回调的队列,我认为这是适当的更高级别的接口。即你会写一些类似的东西:

  singlequeue = Queue()
  incoming_queue1.setcallback(singlequeue.put)
  incoming_queue2.setcallback(singlequeue.put)
  ...
  singlequeue.get()

也许多处理程序包可以增长这个API,但它还没有。这个概念适用于使用术语“频道”而不是“队列”的py.execnet,请参阅此处http://tinyurl.com/nmtr4w

答案 3 :(得分:1)

您可以使用类似Observer模式的内容,其中会向队列订阅者通知状态更改。

在这种情况下,您可以将工作线程指定为每个队列上的侦听器,并且每当它收到就绪信号时,它可以处理新项目,否则休眠。

答案 4 :(得分:1)

不确定多处理队列上的选择在Windows上的效果如何。由于Windows上的select选择侦听套接字而不是文件句柄,我怀疑可能存在问题。

我的回答是让一个线程以阻塞的方式监听每个队列,并将结果全部放入主线程监听的单个队列中,基本上将各个队列复用为一个队列。

我这样做的代码是:

"""
Allow multiple queues to be waited upon.

queue,value = multiq.select(list_of_queues)
"""
import queue
import threading

class queue_reader(threading.Thread):
    def __init__(self,inq,sharedq):
        threading.Thread.__init__(self)
        self.inq = inq
        self.sharedq = sharedq
    def run(self):
        while True:
            data = self.inq.get()
            print ("thread reads data=",data)
            result = (self.inq,data)
            self.sharedq.put(result)

class multi_queue(queue.Queue):
    def __init__(self,list_of_queues):
        queue.Queue.__init__(self)
        for q in list_of_queues:
            qr = queue_reader(q,self)
            qr.start()

def select(list_of_queues):
    outq = queue.Queue()
    for q in list_of_queues:
        qr = queue_reader(q,outq)
        qr.start()
    return outq.get()

以下测试程序显示了如何使用它:

import multiq
import queue

q1 = queue.Queue()
q2 = queue.Queue()

q3 = multiq.multi_queue([q1,q2])

q1.put(1)
q2.put(2)
q1.put(3)
q1.put(4)

res=0
while not res==4:
    while not q3.empty():
        res = q3.get()[1]
        print ("returning result =",res)

希望这有帮助。

托尼华莱士

答案 5 :(得分:1)

以上代码的新版本......

不确定多处理队列上的选择在Windows上的效果如何。由于Windows上的select选择侦听套接字而不是文件句柄,我怀疑可能存在问题。

我的回答是让一个线程以阻塞的方式监听每个队列,并将结果全部放入主线程监听的单个队列中,基本上将各个队列复用为一个队列。

我这样做的代码是:

"""
Allow multiple queues to be waited upon.

An EndOfQueueMarker marks a queue as
    "all data sent on this queue".
When this marker has been accessed on
all input threads, this marker is returned
by the multi_queue.

"""
import queue
import threading

class EndOfQueueMarker:
    def __str___(self):
        return "End of data marker"
    pass

class queue_reader(threading.Thread):
    def __init__(self,inq,sharedq):
        threading.Thread.__init__(self)
        self.inq = inq
        self.sharedq = sharedq
    def run(self):
        q_run = True
        while q_run:
            data = self.inq.get()
            result = (self.inq,data)
            self.sharedq.put(result)
            if data is EndOfQueueMarker:
                q_run = False

class multi_queue(queue.Queue):
    def __init__(self,list_of_queues):
        queue.Queue.__init__(self)
        self.qList = list_of_queues
        self.qrList = []
        for q in list_of_queues:
            qr = queue_reader(q,self)
            qr.start()
            self.qrList.append(qr)
    def get(self,blocking=True,timeout=None):
        res = []
        while len(res)==0:
            if len(self.qList)==0:
                res = (self,EndOfQueueMarker)
            else:
                res = queue.Queue.get(self,blocking,timeout)
                if res[1] is EndOfQueueMarker:
                    self.qList.remove(res[0])
                    res = []
        return res

    def join(self):
        for qr in self.qrList:
            qr.join()

def select(list_of_queues):
    outq = queue.Queue()
    for q in list_of_queues:
        qr = queue_reader(q,outq)
        qr.start()
    return outq.get()

以下代码是我的测试例程,用于说明其工作原理:

import multiq
import queue

q1 = queue.Queue()
q2 = queue.Queue()

q3 = multiq.multi_queue([q1,q2])

q1.put(1)
q2.put(2)
q1.put(3)
q1.put(4)
q1.put(multiq.EndOfQueueMarker)
q2.put(multiq.EndOfQueueMarker)
res=0
have_data = True
while have_data:
    res = q3.get()[1]
    print ("returning result =",res)
    have_data = not(res==multiq.EndOfQueueMarker)

答案 6 :(得分:0)

从Python 3.3开始,您可以使用multiprocessing.connection.wait一次等待多个Queue._reader对象。

答案 7 :(得分:0)

我通常想要多路复用多个队列的一种情况是,每个队列对应于需要不同处理程序的不同类型的消息。你不能只从一个队列中拉取,因为如果它不是你想要的消息类型,你需要把它放回去。

然而,在这种情况下,每个处理程序本质上都是一个单独的消费者,这使得它成为一个多生产者、多消费者的问题。幸运的是,即使在这种情况下,您仍然不需要阻塞多个队列。您可以为每个处理程序创建不同的线程/进程,每个处理程序都有自己的队列。基本上,您可以将其分解为多生产者、单消费者问题的多个实例。

我能想到的唯一一种情况是,您必须在多个队列上等待,如果您被迫将多个处理程序放在同一个线程/进程中。在这种情况下,我会通过为我的主线程创建一个队列来重构它,为每个处理程序生成一个线程,并让处理程序使用主队列与主线程通信。然后,每个处理程序都可以为其独特的消息类型设置一个单独的队列。

答案 8 :(得分:-3)

不要这样做。

在邮件上放置标题并将其发送到公共队列。这简化了代码并且整体更清晰。