【问题标题】:Tornado non-blocking requestTornado 非阻塞请求
【发布时间】:2013-11-12 01:37:26
【问题描述】:

我有一个需要一段时间才能运行的函数,并且需要在请求中运行它。处理此问题的最佳方法是什么,以便此请求在处理时不会阻塞主线程?我查看了 @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"

【问题讨论】:

    标签: python asynchronous tornado


    【解决方案1】:

    当你有一些阻塞任务不适合异步事件循环时,你必须把它放在一个单独的线程中。

    如果您要拥有无限数量的阻塞任​​务,则需要使用线程池。

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

    最简单的方法是使用像 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 中完成,而不是在像 numpy 那样释放 GIL 的 C 扩展中),您必须将它在一个单独的过程中。快速搜索 Tornado 进程池库并没有找到那么多不错的选择,但是在 Python 中将线程池代码调整为进程池代码通常非常容易。**


    * 请注意,我并不是特别推荐该库;它只是在 Google 搜索中出现的第一件事,一眼看去它看起来可用且正确。

    ** 这通常就像用concurrent.futures.ProcessPoolExecutor 替换concurrent.futures.ThreadPoolExecutor 或用multiprocessing.Pool 替换multiprocessing.dummy.Pool 一样简单。唯一的技巧是确保所有任务参数和返回值都小且可腌制。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 谢谢,我看到了另一个类似策略的问题/答案,但在这种情况下,我无法控制函数的工作方式;我就这么称呼它。
      猜你喜欢
      • 2021-08-28
      • 2012-10-14
      • 1970-01-01
      • 2014-07-29
      • 2023-03-22
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 2013-01-26
      相关资源
      最近更新 更多