16

私は最近、フラスコを使用してPythonでペットプロジェクトに取り組んでいます。これは、pygments を使用したサーバー側の構文強調表示をサポートするシンプルなペーストビンです。これはコストのかかるタスクであるため、構文の強調表示をセロリ タスク キューに委任し、リクエスト ハンドラーでそれが完了するのを待ちます。言うまでもなく、これは別のワーカーの CPU 使用率を軽減するだけです。結果を待っていると Web サーバーへの接続がロックされてしまうからです。疫病のような時期尚早の最適化を避けるように私に言っている私の本能にもかかわらず、私はまだ非同期を調べずにはいられませんでした。

非同期

最近 Python Web 開発をフォローしている場合は、async がどこにでもあることを見たことがあるはずです。非同期が行うことは、協調マルチタスキングを復活させることです。つまり、各「スレッド」がいつどこで別のスレッドに譲るかを決定します。この非プリエンプティブ プロセスは OS スレッドよりも効率的ですが、それでも欠点があります。現時点では、次の 2 つの主要なアプローチがあるようです。

  • イベント/コールバック スタイルのマルチタスク
  • コルーチン

1 つ目は、イベント ループで実行される疎結合コンポーネントによって同時実行性を提供します。これは競合状態に関してはより安全であり、より一貫性を提供しますが、プリエンプティブ マルチタスクよりもはるかに直感的でなく、コーディングが困難です。

もう 1 つは、より伝統的なソリューションで、スレッド プログラミング スタイルに近く、プログラマはコンテキストを手動で切り替えるだけで済みます。競合状態やデッドロックが発生しやすくなりますが、簡単なドロップイン ソリューションを提供します。

現在、ほとんどの非同期作業は、 IO バウンドタスクと呼ばれる、入力または出力の待機をブロックするタスクで行われています。これは通常、呼び出すことができるポーリングおよびタイムアウト ベースの関数を使用することで実現されます。それらが否定的に返された場合は、コンテキストを切り替えることができます。

名前にかかわらず、これはCPU バウンドのタスクにも適用できます。このタスクは別のワーカー (スレッド、プロセスなど) に委譲され、ノンブロッキングで解放されるのを待つことができます。理想的には、これらのタスクは非同期に適した方法で記述されますが、現実的には、コードをブロックしないように十分に小さなチャンクに分割することを意味し、できればコード行ごとにコンテキスト スイッチを分散させないようにする必要があります。これは、既存の同期ライブラリにとって特に不便です。


利便性のため、非同期作業に gevent を使用することにしましたが、非同期環境で CPU にバインドされたタスクをどのように処理するのか疑問に思っていました (先物、セロリなどを使用していますか?)。

フラスコなどの従来の Web フレームワークで非同期実行モデル (この場合は gevent) を使用するにはどうすればよいですか? 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 または Multiprocessing ライブラリが役立つかもしれません。

私は gevent にあまり詳しくありませんが、必要な複雑さや理由は想像できません。

つまり、主要な世界の Web サイトの効率を実現しようとしている場合は、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 に答える