龙卷风非阻塞请求

时间:2013-10-31 21:21:28

标签: python asynchronous tornado

我有一个需要一段时间才能运行的函数,需要在请求中运行它。处理此问题的最佳方法是什么,以便此请求在处理时不会阻塞主线程?我查看了@tornado.web.asynchronous装饰器,但是当函数不是异步龙卷风模块时,这没什么用处。

class LongHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(self.long_time_function())

    def long_time_function(self):
        time.sleep(5)
        return "foo"

2 个答案:

答案 0 :(得分:7)

如果你有一些阻塞任务对async事件循环不起作用,你必须把它放在一个单独的线程中。

如果您要进行无限数量的阻止任务,则需要使用线程池。

无论哪种方式,您都希望有一个包装器异步任务阻止来自线程任务的通知。

最简单的方法是使用预先构建的库,例如tornado-threadpool。*然后,您只需执行以下操作:

class LongHandler(tornado.web.RequestHandler):
    @thread_pool.in_thread_pool
    def long_time_function(self, callback):
        time.sleep(5)
        callback("foo")

如果你想自己做,this gist显示了你必须做的事情的一个例子 - 或者,当然,各种Tornado线程池库的源代码可以作为示例代码。

请记住Python GIL的局限性:如果你的后台任务是受CPU限制的(而且大部分工作是用Python完成的,而不是像Cump那样释放GIL就像numpy那样),你必须把它放在它在一个单独的过程中。快速搜索Tornado进程池库并没有找到很多不错的选项,但是在Python中调整线程池代码来处理池代码通常非常容易。**


*请注意,我并不是特别推荐该图书馆;这只是谷歌搜索中出现的第一件事,从快速浏览一下看起来很有用和正确。

**通常只需将concurrent.futures.ThreadPoolExecutor替换为concurrent.futures.ProcessPoolExecutor或将multiprocessing.dummy.Pool替换为multiprocessing.Pool即可。唯一的技巧是确保所有的任务参数和返回值都很小并且可以选择。

答案 1 :(得分:1)

使用其他答案中所述的其他线程是一种方法。如果您的函数可以拆分为片段(例如,内部有一个循环有很多迭代),您还可以使用IOLoop.add_callback()将其拆分,以便通过处理其他请求来交织它。以下是如何执行此操作的示例:why my coroutine blocks whole tornado instance?