非阻塞multiprocessing.connection.Listener?

时间:2016-08-13 20:36:20

标签: python sockets asynchronous python-3.3 python-multiprocessing

我使用multiprocessing.connection.Listener进行进程之间的通信,它对我来说是一个魅力。现在我真的很喜欢我的mainloop在客户端的命令之间做一些其他事情。不幸的是,listener.accept()会阻止执行,直到建立客户端进程的连接为止。

是否有一种简单的方法来管理multiprocessing.connection的非阻塞检查?超时?或者我应该使用专用线程吗?

    # Simplified code:

    from multiprocessing.connection import Listener

    def mainloop():
        listener = Listener(address=(localhost, 6000), authkey=b'secret')

        while True:
            conn = listener.accept() # <---  This blocks!
            msg = conn.recv() 
            print ('got message: %r' % msg)
            conn.close()

4 个答案:

答案 0 :(得分:1)

我发现的一个解决方案(尽管它可能不是最“优雅”的解决方案)正在使用conn.poll。(documentation)如果侦听器有新数据,则轮询将返回True,并且(最重要的是)如果没有传递任何参数,它就是非阻塞的。我不是100%地确定这是最好的方法,但是我成功运行了一次,只运行一次listener.accept(),然后使用以下语法重复获取输入(如果有的话)

from multiprocessing.connection import Listener

def mainloop():
    running = True

    listener = Listener(address=(localhost, 6000), authkey=b'secret')
    conn = listener.accept()
    msg = ""

    while running:
        while conn.poll():
            msg = conn.recv() 
            print (f"got message: {msg}")

            if msg == "EXIT":
                running = False

        # Other code can go here
        print(f"I can run too! Last msg received was {msg}")

     conn.close()

如果您一次只想获取最多一条消息,则条件语句中的“ while”可以替换为“ if”。请谨慎使用,因为它似乎有点“ hacky”,我在其他地方也没有找到为此目的而使用conn.poll的引用。

答案 1 :(得分:0)

我自己没有使用过Listener对象 - 我通常使用multiprocessing.Queue这个任务; doco在以下链接:

https://docs.python.org/2/library/queue.html#Queue.Queue

该对象可用于在Python进程之间使用漂亮的API发送和接收任何可pickle对象;我想你最感兴趣的是:

  • 在流程A中
    • .put('some message')
  • 过程B中的
    • .get_nowait() # will raise Queue.Empty if nothing is available- handle that to move on with your execution

唯一的限制是你需要在某个时刻控制两个Process对象,以便能够将队列分配给它们 - 如下所示:

import time
from Queue import Empty
from multiprocessing import Queue, Process


def receiver(q):
    while 1:
        try:
            message = q.get_nowait()
            print 'receiver got', message
        except Empty:
            print 'nothing to receive, sleeping'
            time.sleep(1)


def sender(q):
    while 1:
        message = 'some message'
        q.put('some message')
        print 'sender sent', message
        time.sleep(1)


some_queue = Queue()

process_a = Process(
    target=receiver,
    args=(some_queue,)
)

process_b = Process(
    target=sender,
    args=(some_queue,)
)

process_a.start()
process_b.start()

print 'ctrl + c to exit'
try:
    while 1:
        time.sleep(1)
except KeyboardInterrupt:
    pass

process_a.terminate()
process_b.terminate()

process_a.join()
process_b.join()

队列很好,因为你可以拥有尽可能多的消费者和许多生产者,以获得完全相同的队列对象(方便分发任务)。

我应该指出,只是在进程上调用.terminate()是不好的形式 - 你应该使用闪亮的新消息系统来传递关闭消息或那种性质的东西。

答案 2 :(得分:0)

多处理模块具有一个不错的功能,称为Pipe()。这是在两个进程之间共享资源的好方法(以前从未尝试过两个以上)。随着python 3.80的到来,多处理模块中出现了共享内存功能,但是我还没有真正测试过,因此我无法为其提供担保 您将使用

之类的管道功能
from multiprocessing import Pipe

.....

def sending(conn):
    message = 'some message'
    #perform some code
    conn.send(message)
    conn.close()

receiver, sender = Pipe()
p = Process(target=sending, args=(sender,))
p.start()
print receiver.recv()   # prints "some message"
p.join()

有了这个,您应该能够有独立运行的单独进程,并且当您需要一个进程的输入时。如果由于另一个进程的数据无法获取而导致某种错误,则可以将其置于某种睡眠或停止状态,或者使用while循环不断检查另一个进程完成该任务并通过其发送时的挂起状态

while not parent_conn.recv():
    time.sleep(5)

这应该使它处于无限循环状态,直到其他进程运行完毕并发送结果为止。这也比Queue快2-3倍。尽管就个人而言,队列也是一个不错的选择,但我不使用它。

答案 3 :(得分:0)

您可以在线程中运行阻止功能:

conn = await loop.run_in_executor(None, listener.accept)