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

在多个Python多处理队列上选择?,python,events,select,synchronization,multiprocessing,Python,Events,Select,Synchronization,Multiprocessing,如果两个多处理中的任何一个都驻留在同一个系统上,那么最好的等待方式是什么而不旋转,直到有东西可用?您可以使用类似的模式,其中队列订阅者将收到状态更改的通知 在这种情况下,您可以将您的工作线程指定为每个队列上的侦听器,并且每当它收到就绪信号时,它就可以处理新项目,否则将处于休眠状态。看起来还没有正式的方法来处理此问题。或者至少,不是基于这个: 您可以尝试类似本文所做的操作—访问底层管道文件句柄: 然后使用select。在以独立于平台的方式使用多处理时,使用线程将传入的项目转发到一个队列,然后等待该

如果两个多处理中的任何一个都驻留在同一个系统上,那么最好的等待方式是什么而不旋转,直到有东西可用?

您可以使用类似的模式,其中队列订阅者将收到状态更改的通知


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

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

您可以尝试类似本文所做的操作—访问底层管道文件句柄:


然后使用select。

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

避免线程需要处理低级管道/FD,这两种管道都是特定于平台的,并且不容易与高级API一致处理

或者您需要能够设置回调的队列,我认为这是合适的更高级别接口。也就是说,你会写下如下内容:

singlequeue = Queue() incoming_queue1.setcallback(singlequeue.put) incoming_queue2.setcallback(singlequeue.put) ... singlequeue.get()
也许多处理软件包可以扩展这个API,但它还没有实现。这个概念适用于py.execnet,它使用术语channel而不是queues,请参见此处

实际上,您可以使用select.select中的multiprocessing.Queue对象。i、 e

que = multiprocessing.Queue()
(input,[],[]) = select.select([que._reader],[],[])
仅当可从中读取que时,才会选择que

但是没有关于它的文档。我当时正在读linux上multiprocessing.queue库的源代码,通常是/usr/lib/python2.6/multiprocessing/queue.py之类的代码


对于Queue.Queue,我没有找到任何明智的方法来实现这一点,我真的很想这样做。

不确定在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()
"""
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)

res=0
while not res==4:
    while not q3.empty():
        res = q3.get()[1]
        print ("returning result =",res)
希望这有帮助


托尼·华莱士(Tony Wallace)

上述代码的新版本


不确定多处理队列上的select在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()
"""
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)
不要这样做


在消息上放置一个头并将其发送到公共队列。这简化了代码,并且总体上更加清晰

从Python3.3开始,您可以使用它同时等待多个队列。_reader对象。

嗯,get是破坏性的,因此您不能像GoF描述的那样真正观察队列本身。出列线程必须是被观察到的-我希望比另外两个线程的开销要少。另外,如果我想要像select中那样的调用进程的单点访问,我需要在这两个线程之上有一个线程安全队列。这将是一个非常好的接口!尽管保持stdlib接口紧密显然有好处,正如Jesse在@ars引用的bug report.true中提到的那样,但是当前的队列公共API不能处理您的用例,我认为这是一个常见的用例。如果它是通用的,请在bugs.python.org上提交一个bug report+补丁,并在2.7/3.x上对其进行评估。这在Unix上非常有效,但是在Windows上,select.select实现只能处理套接字,不能处理文件描述符,因此失败。Queue.Queue和multiprocessing.Queue之间的主要区别是什么,多处理队列可以用于多线程而不仅仅是多处理吗?@cmcdragokai我认为Queue.Queue是阻塞队列的数据结构;队列周围的同步。队列依赖于互斥。queue.queue下面没有文件描述符或任何类似的东西,因此我们无法通过操作系统调用(如select、epoll、kqueue)来等待它。@AndreHolzner有一个工作版本:假设我们在一个系统中为不同的消息类型使用不同的处理程序。如果他们都从同一队列中读取,那么他们需要放回所有不适合他们的消息。在我的情况下,退一步重新考虑帮助我使代码更干净、更健壮。我有两个生产商为了一个消费者而争吵。只有一个队列等待是正确的做法。