执行multiprocessing.Queue和queue.Queue

时间:2017-07-17 15:38:04

标签: python linux queue pipe

我正在寻找有关Python中Queues实现的更多见解,而不是我在文档中找到的。

根据我的理解,如果我错了,请原谅我的无知:

queue.Queue():通过内存中的基本数组实现,因此不能在多个进程之间共享,但可以在线程之间共享。到目前为止,非常好。

multiprocessing.Queue():是通过管道(man 2 pipes)实现的,这些管道具有大小限制(相当小:在Linux上,man 7 pipe表示65536未经处理):

  

自Linux 2.6.35起,默认管道容量为65536字节,但可以使用fcntl(2) F_GETPIPE_SZF_SETPIPE_SZ操作查询和设置容量

但是,在Python中,每当我尝试将大于65536字节的数据写入管道时,它都可以毫无例外地工作 - 我可以通过这种方式充斥我的记忆:

import multiprocessing
from time import sleep

def big():
    result = ""
    for i in range(1,70000):
        result += ","+str(i)
    return result # 408888 bytes string

def writequeue(q):
    while True:
        q.put(big())
        sleep(0.1)

if __name__ == '__main__':
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=writequeue, args=(q,))
    p.start()
    while True:
        sleep(1) # No pipe consumption, we just want to flood the pipe

所以这是我的问题:

  • Python会调整管道限制吗?如果是的话,多少钱?欢迎使用Python源代码。

  • Python管道通信是否可以与其他非Python进程互操作?如果是,欢迎工作示例(最好是JS)和资源链接。

1 个答案:

答案 0 :(得分:10)

为什么q.put()没有阻塞?

mutiprocessing.Queue创建一个管道,如果管道已满,则会阻塞。当然,写入超过管道容量将导致write调用阻塞,直到读取端清除了足够的数据。好的,所以如果管道在达到其容量时阻塞,为什么管道满了时q.put() 阻塞?即使第一次调用示例中的q.put()也应该填满管道,一切都应该阻塞,不是吗?

不,它不会阻止,因为multiprocessing.Queue实现将.put()方法与写入管道解耦。 .put()方法将传递的数据排入队列在内部缓冲区中,有一个单独的线程,负责读取此缓冲区并写入管道。管道已满时,此线程将阻塞,但不会阻止.put()将更多数据排入内部缓冲区。

.put()的实施将数据保存到self._buffer,并注意如果没有一个线程已经在运行,它将如何启动线程:

def put(self, obj, block=True, timeout=None):
    assert not self._closed
    if not self._sem.acquire(block, timeout):
        raise Full

    with self._notempty:
        if self._thread is None:
            self._start_thread()
        self._buffer.append(obj)
        self._notempty.notify()

._feed()方法是从self._buffer读取并将数据提供给管道的方法。而._start_thread()是设置运行._feed()

的线程的原因

如何限制队列大小?

如果你想限制可以写入队列的数据量,我不会通过指定字节数看到一种方法,但你可以限制存储在内部的项目数量通过将数字传递给multiprocessing.Queue

来缓冲任何时间
q = multiprocessing.Queue(2)

当我使用上面的参数并使用您的代码时,q.put()会将两个项目排入队列,并在第三次尝试时阻止。

Python管道通信是否可与其他非Python进程互操作?

这取决于。 multiprocessing模块提供的工具不易与其他语言互操作。我希望可能使multiprocessing与其他语言互操作,但实现这一目标将是一个重要的事业。编写该模块时期望所涉及的进程正在运行Python代码。

如果你看一下更通用的方法,那么答案是肯定的。您可以使用套接字作为两个不同进程之间的通信管道。例如,从命名套接字读取的JavaScript进程:

var net = require("net");
var fs = require("fs");

sockPath = "/tmp/test.sock"
try {
    fs.unlinkSync(sockPath);
}
catch (ex) {
    // Don't care if the path does not exist, but rethrow if we get
    // another error.
    if (ex.code !== "ENOENT") {
        throw ex;
    }
}

var server = net.createServer(function(stream) {
  stream.on("data", function(c) {
    console.log("received:", c.toString());
  });

  stream.on("end", function() {
    server.close();
  });
});

server.listen(sockPath);

写入它的Python进程:

import socket
import time

sockfile = "/tmp/test.sock"

conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
conn.connect(sockfile)

count = 0
while True:
    count += 1
    conn.sendall(bytes(str(count), "utf-8"))
    time.sleep(1)

如果你想尝试上面的内容,你需要先启动JavaScript端,以便Python端有东西可以写入。这是一个概念验证。一个完整的解决方案需要更多的润色。

为了将复杂的结构从Python传递到其他语言,您必须找到一种以双面可读取的格式序列化数据的方法。不幸的是,Pickles特定于Python。我通常在需要在语言之间进行序列化时选择JSON,或者如果JSON不能这样做,则使用ad-hoc格式。