Python 收集Tornado和x27的增量结果;进程池执行器

Python 收集Tornado和x27的增量结果;进程池执行器,python,multiprocessing,tornado,blinker,Python,Multiprocessing,Tornado,Blinker,我有一个tornado应用程序,它需要在ProcessPoolExecutor上运行阻塞功能。此阻塞功能使用一个库,该库通过闪烁事件发出增量结果。我想收集这些事件,并在它们发生时将它们发送回我的tornado应用程序 起初,tornado似乎非常适合这个用例,因为它是异步的。我想我可以简单地将一个tornado.queues.Queue对象传递给要在池中运行的函数,然后将put()事件作为blinker事件回调的一部分放到这个队列中 然而,通过阅读tornado.queues.Queue的文档,

我有一个
tornado
应用程序,它需要在
ProcessPoolExecutor
上运行阻塞功能。此阻塞功能使用一个库,该库通过
闪烁
事件发出增量结果。我想收集这些事件,并在它们发生时将它们发送回我的
tornado
应用程序

起初,
tornado
似乎非常适合这个用例,因为它是异步的。我想我可以简单地将一个
tornado.queues.Queue
对象传递给要在池中运行的函数,然后将
put()
事件作为
blinker
事件回调的一部分放到这个队列中

然而,通过阅读
tornado.queues.Queue
的文档,我了解到它们不是跨进程管理的,比如
multiprocessing.Queue
,并且不是线程安全的

有没有办法在这些事件发生时从
池中检索它们?我是否应该包装
多处理.Queue
以便它生成
未来
?这似乎不太可能奏效,因为我怀疑
多处理
的内部结构是否与
龙卷风
兼容

[编辑]
这里有一些很好的线索:

你可以做得更简单一些。下面是一个协同程序,它向子进程提交四个缓慢的函数调用并等待它们:

from concurrent.futures import ProcessPoolExecutor
from time import sleep

from tornado import gen, ioloop

pool = ProcessPoolExecutor()


def calculate_slowly(x):
    sleep(x)
    return x


async def parallel_tasks():
    # Create futures in a randomized order.
    futures = [gen.convert_yielded(pool.submit(calculate_slowly, i))
               for i in [1, 3, 2, 4]]

    wait_iterator = gen.WaitIterator(*futures)
    while not wait_iterator.done():
        try:
            result = await wait_iterator.next()
        except Exception as e:
            print("Error {} from {}".format(e, wait_iterator.current_future))
        else:
            print("Result {} received from future number {}".format(
                result, wait_iterator.current_index))


ioloop.IOLoop.current().run_sync(parallel_tasks)
它输出:

Result 1 received from future number 0
Result 2 received from future number 2
Result 3 received from future number 1
Result 4 received from future number 3
您可以看到,协同程序按照结果完成的顺序接收结果,而不是按照提交的顺序接收结果:未来1号在未来2号之后解析,因为未来1号睡眠时间更长。convert将ProcessPoolExecutor返回的期货转换为Tornado兼容的期货,可在协同程序中等待

每个future解析为calculate\u slow返回的值:在本例中,它与缓慢传递到calculate\u的数字相同,与calculate\u缓慢休眠的秒数相同

要将其包含在RequestHandler中,请尝试以下操作:

class MainHandler(web.RequestHandler):
    async def get(self):
        self.write("Starting....\n")
        self.flush()

        futures = [gen.convert_yielded(pool.submit(calculate_slowly, i))
                   for i in [1, 3, 2, 4]]

        wait_iterator = gen.WaitIterator(*futures)
        while not wait_iterator.done():
            result = await wait_iterator.next()
            self.write("Result {} received from future number {}\n".format(
                result, wait_iterator.current_index))

            self.flush()


if __name__ == "__main__":
    application = web.Application([
        (r"/", MainHandler),
    ])
    application.listen(8888)
    ioloop.IOLoop.instance().start()

如果您
curl localhost:8888
,则可以观察到服务器以增量方式响应客户端请求。

若要收集传递给
ProcessPoolExecutor
的任务的返回值以外的任何内容,则必须使用
多处理.Queue
(或
多处理
库中的其他对象)。然后,由于
multiprocessing.Queue
只公开了一个同步接口,因此您必须在父进程中使用另一个线程从队列中读取(而不涉及实现细节。这里可以使用一个文件描述符,但我们暂时忽略它,因为它没有文档记录并且可能会更改)

下面是一个未经测试的快速示例:

queue = multiprocessing.Queue()
proc_pool = concurrent.futures.ProcessPoolExecutor()
thread_pool = concurrent.futures.ThreadPoolExecutor()

async def read_events():
    while True:
        event = await thread_pool.submit(queue.get)
        print(event)

async def foo():
    IOLoop.current.spawn_callback(read_events)
    await proc_pool.submit(do_something_and_write_to_queue)

我不是这个意思。为了澄清,我有一个缓慢的函数调用。并且,该函数调用通过
闪烁器发出大量事件。我想收集这些事件并将它们发送回龙卷风主线。我相信你这里的代码并行运行四个慢函数,而不是一个慢函数。是的,这或多或少是我最后做的,但是使用了
aioprocessing
模块,它支持非阻塞
AioQueue.coro_get