【问题标题】:Python async and CPU-bound tasks?Python 异步和 CPU 密集型任务?
【发布时间】:2013-04-12 10:38:47
【问题描述】:

我最近一直在使用烧瓶在 python 中进行一个宠物项目。它是一个简单的 pastebin,带有 pygments 的服务器端语法高亮支持。因为这是一项代价高昂的任务,所以我将语法突出显示委托给 celery 任务队列,并在请求处理程序中等待它完成。不用说,这只不过是减轻了另一个工作人员的 CPU 使用率,因为等待结果仍然会锁定与 Web 服务器的连接。 尽管我的直觉告诉我要避免像瘟疫一样过早的优化,但我还是忍不住研究了异步。

异步

如果最近一直在关注 python web 开发,你肯定已经看到异步无处不在。 async 所做的是带回协作多任务,这意味着每个“线程”决定何时何地让步给另一个。这种非抢占式进程比操作系统线程更有效,但仍然有它的缺点。目前似乎有两种主要方法:

  • 事件/回调风格的多任务处理
  • 协程

第一个通过在事件循环中执行的松散耦合组件提供并发性。尽管这在竞争条件方面更安全并提供了更高的一致性,但与抢先式多任务处理相比,它的直观性和编码难度要小得多。

另一种是更传统的解决方案,更接近线程编程风格,程序员只需手动切换上下文。虽然更容易出现竞争条件和死锁,但它提供了一个简单的解决方案。

目前大多数异步工作都是在所谓的 IO-bound 任务上完成的,这些任务会阻塞以等待输入或输出。这通常是通过使用可以调用的轮询和基于超时的函数来完成的,如果它们返回负数,则可以切换上下文。

尽管有这个名字,这也可以应用于CPU-bound任务,可以委托给另一个工作人员(线程、进程等),然后非阻塞地等待屈服。理想情况下,这些任务将以异步友好的方式编写,但实际上这意味着将代码分成足够小的块以防止阻塞,最好不要在每一行代码之后分散上下文切换。这对于现有的同步库来说尤其不方便。


由于方便,我决定使用 gevent 进行异步工作,并想知道如何在异步环境中处理 CPU 密集型任务(使用期货、芹菜等?)。

如何在flask等传统Web框架中使用异步执行模型(本例中为gevent)? python(futures、task queues)中这些问题有哪些普遍认可的解决方案?

编辑:更具体地说 - 如何将 gevent 与烧瓶一起使用以及如何在这种情况下处理 CPU 密集型任务?

EDIT2:考虑到 Python 的 GIL 会阻止线程代码的最佳执行,这仅留下多处理选项,至少在我的情况下如此。这意味着要么使用 concurrent.futures 或其他一些处理处理的外部服务(甚至可以为与语言无关的东西打开大门)。在这种情况下,gevent( celery)的一些流行或经常使用的解决方案是什么? - 最佳实践

【问题讨论】:

  • 您几乎可以在每个库中采用这种模式:bottlepy.org/docs/dev/async.html#event-callbacks。我建议evergreen,因为它允许通过集成concurrent.futures 的修改版本将协作任务(greenlets)与长时间运行的任务结合起来。

标签: python asynchronous flask gevent


【解决方案1】:

执行以下操作将 cpu 密集型任务分成异步线程应该是线程安全的:

from threading import Thread

def send_async_email(msg):
    mail.send(msg)

def send_email(subject, sender, recipients, text_body, html_body):
    msg = Message(subject, sender = sender, recipients = recipients)
    msg.body = text_body
    msg.html = html_body
    thr = Thread(target = send_async_email, args = [msg])
    thr.start()

如果您需要更复杂的东西,那么 Flask-Celery 或带有“Pool”的多处理库可能对您有用。

我对 gevent 不太熟悉,但我无法想象您可能需要更多复杂性或为什么。

我的意思是,如果您想获得大型世界网站的效率,那么我建议您构建 C++ 应用程序来完成 CPU 密集型工作,然后使用 Flask-celery 或 Pool 来运行该进程。 (这就是 YouTube 在混合 C++ 和 Python 时所做的)

【讨论】:

  • 关于 Youtube 的任何来源(博客或其他内容)?
  • 我最终使用了 celery,但正如我所说,等待结果最终会阻塞网络服务器。解决方案是使用 gevent 或 gunicorn 服务器使用 gevent worker 为 WSGI 应用程序提供服务。对于异步结果,我只需轮询 ready(),如果没有完成,我会使用规范的 gevent.sleep() 产生。
  • 恐怕我没有链接,但谷歌博客可能是一个寻找它的地方。
【解决方案2】:

简单地使用 ThreadPool 和 Queue 怎么样?然后,您可以以同步方式在单独的线程中处理您的东西,您根本不必担心阻塞。好吧,Python 首先不适合 CPU 密集型任务,因此您还应该考虑生成子进程。

【讨论】:

  • 这是一个有效的解决方案,我还可以考虑其他几个。关键是,我想知道如何将 gevent 集成到烧瓶中,以及使用 gevent 的人如何处理无法使同步任务“绿色”的情况。
猜你喜欢
  • 2023-02-07
  • 1970-01-01
  • 2020-10-11
  • 2020-06-16
  • 2017-07-25
  • 2014-08-03
  • 2013-07-31
  • 1970-01-01
  • 2017-04-10
相关资源
最近更新 更多