【问题标题】:How to use python tornado @gen.coroutine with long running functions如何使用 python tornado @gen.coroutine 和长时间运行的函数
【发布时间】:2020-05-29 21:55:33
【问题描述】:

我有一个 Web 应用程序也在进行非常密集的数据处理。 有些功能非常慢(想想几分钟)。

到目前为止,我的架构是为每个连接生成新的线程/进程,因此这些慢速功能不会阻塞其他用户。但这会消耗太多内存,而且不符合 tornado 架构。

所以我想知道这种问题是否有解决方案。 我的代码如下所示:

# code that is using to much memory because of the new threads being spawned 
def handler():
   thread = Thread(target = really_slow_function)
   thread.start()
   thread.join()
   return "done"

def really_slow_function():
   # this is an example of an intensive function
   # which should be treated as a blackbox
   sleep(100)
   return "done"

重构后我有以下代码:

#code that doesn't scale because all the requests are block on that one slow request.
@gen.coroutine
def handler():
   yield really_slow_function()
   raise gen.Return("done")

def really_slow_function():
   # this is an example of an intensive function
   # which should be treated as a blackbox
   sleep(100)
   return "done"

此重构的问题在于,tornado 服务器阻塞了really_slow_function,同时无法为其他请求提供服务。

所以问题是:有没有一种方法可以在不触及really_slow_function 并且不创建新线程/进程的情况下重构处理程序?

【问题讨论】:

  • 阻塞函数需要在单独的线程中运行。没有办法解决它。

标签: python multithreading asynchronous tornado


【解决方案1】:

使用ThreadPoolExecutor(来自concurrent.futures 包)在单独的线程中运行长时间运行的函数,而无需每次都启动一个新线程。

async def handler():
    await IOLoop.current().run_in_executor(None, really_slow_function)
    return "done"

如果你想准确控制有多少线程有资格运行这个函数,你可以创建自己的执行器并传递它而不是None

【讨论】:

  • 谢谢,看起来不错!如果我可以进一步推动这个问题。您知道是否可以在“执行程序”中进行某种路由?所以我可以根据它们的使用重新使用内存中已经有某种对象的线程吗?谢谢!
  • 不是真的; Executor 接口的重点是你不关心特定的线程。如果您确实关心特定线程,则可能需要自行创建和管理它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-13
  • 2017-08-05
  • 1970-01-01
  • 2013-08-24
  • 1970-01-01
  • 1970-01-01
  • 2016-08-09
相关资源
最近更新 更多