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
。