从multiprocessing.Queue获得接近LIFO行为的干净方法? (甚至只是*不*近FIFO)

时间:2012-08-20 17:55:10

标签: python multiprocessing

是否有人知道从multiprocessing.Queue获得接近LIFO甚至不接近FIFO(例如随机)行为的干净方法?

备选问题:有人可以指出管理multiprocessing.Queue背后实际存储结构的线程的代码吗?似乎提供近似LIFO访问权限的内容似乎很简单但是我迷失在试图找到它的兔子洞里。

注意:

  1. 我相信multiprocessing.Queue does not guarantee order。精细。但它接近FIFO,所以接近LIFO会很棒。
  2. 我可以将所有当前项目从队列中取出并在使用之前撤消订单,但如果可能的话,我更愿意避免使用kludge。

  3. (编辑)澄清:我正在使用multiprocessing进行CPU绑定模拟,因此无法使用Queue中的专用队列。由于我几天没有看到任何答案,我已经在上面添加了替代问题。


    如果是一个问题,下面是multiprocessing.Queue接近FIFO的轻微证据。它只是表明在一个简单的情况下(单个线程),它在我的系统上是完美的FIFO:

    import multiprocessing as mp
    import Queue
    
    q = mp.Queue()
    
    for i in xrange(1000):
        q.put(i)
    
    deltas = []
    while True:
        try:
            value1 = q.get(timeout=0.1)
            value2 = q.get(timeout=0.1)
            deltas.append(value2-value1)
        except Queue.Empty:
            break
    
    #positive deltas would indicate the numbers are coming out in increasing order
    min_delta, max_delta = min(deltas), max(deltas)
    avg_delta = sum(deltas)/len(deltas)
    
    print "min", min_delta
    print "max", max_delta
    print "avg", avg_delta
    

    打印:min,max和average正好为1(完美FIFO)

2 个答案:

答案 0 :(得分:2)

我查看了我的Python安装中的Lib/multiprocessing/queues.py中的Queue类(Python 2.7,但是我简要检查过的Python 3.2版本没有任何明显的不同)。以下是我理解它的工作原理:

Queue对象维护有两组对象。一组是由所有进程共享的多进程安全原型。其他过程由每个过程单独创建和使用。

跨进程对象在__init__方法中设置:

  1. Pipe个对象,其两端保存为self._readerself._writer
  2. 一个BoundedSemaphore对象,它计算(并可选地限制)队列中有多少个对象。
  3. 用于读取管道的Lock对象,在非Windows平台上用于写入管道。 (我认为这是因为在Windows上写入管道本质上是多进程安全的。)
  4. 每个进程对象在_after_fork_start_thread方法中设置:

    1. 用于缓冲对管道写入的collections.deque对象。
    2. 用于在缓冲区不为空时发出信号的threading.condition对象。
    3. 执行实际写作的threading.Thread对象。它是懒惰创建的,因此在给定进程中至少要求对Queue进行一次写入之前它不会存在。
    4. 在流程结束时清理内容的各种Finalize个对象。
    5. 队列中的get非常简单。您获取读锁定,递减信号量,并从管道的读取端抓取一个对象。

      put更复杂。它使用多个线程。 put的调用者获取条件的锁定,然后将其对象添加到缓冲区并在解锁之前发出信号。它还会增加信号量并启动编写器线程(如果它还没有运行)。

      编写器线程在_feed方法中永远循环(直到取消)。如果缓冲区为空,则等待notempty条件。然后它从缓冲区获取一个项目,获取写锁定(如果存在)并将该项目写入管道。


      所以,鉴于所有这些,您可以修改它以获得LIFO队列吗?这看起来并不容易。管道本质上是FIFO对象,虽然队列不能保证整体的FIFO行为(由于来自多个进程的写入的异步性质),但它总是主要是FIFO。

      如果您只有一个使用者,则可以从队列中get个对象并将它们添加到您自己的进程本地堆栈中。使用多用户堆栈会更难,但是对于共享内存,有限大小的堆栈也不会太难。您需要一个锁,一对条件(用于在完全和空状态下阻塞/发信号),共享整数值(对于保持的值的数量)和适当类型的共享数组(对于值本身)。

答案 1 :(得分:1)

Queue包中有LIFO queue(Python 3中的队列)。这不会在多处理或multiprocessing.queues模块中公开。

q = mp.Queue()替换你的行q = Queue.LifoQueue()并运行print:min,max和average正好为-1。

(另外我认为在从一个线程获取项目时,您应该始终获得完全FIFO / LIFO顺序。)