16

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

异步

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

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

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

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

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

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


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

如何将异步执行模型(在本例中为 gevent)与烧瓶等传统 Web 框架一起使用?python(期货,任务队列)中这些问题的一些普遍同意的解决方案是什么?

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

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

4

2 回答 2

7

执行以下操作将 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 时所做的)

于 2013-04-12T19:49:23.630 回答
2

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

于 2013-04-12T11:58:53.657 回答