1. ホーム
  2. python

[解決済み] Flask でグローバル変数はスレッドセーフか?リクエスト間でデータを共有するには?

2022-07-19 17:36:28

質問

私のアプリケーションでは、共通のオブジェクトの状態がリクエストによって変更され、レスポンスはその状態に依存します。

class SomeObj():
    def __init__(self, param):
        self.param = param
    def query(self):
        self.param += 1
        return self.param

global_obj = SomeObj(0)

@app.route('/')
def home():
    flash(global_obj.query())
    render_template('index.html')

これを開発用サーバで実行すると、1、2、3、...と表示されると思います。 100 の異なるクライアントから同時にリクエストが行われた場合、何か問題が発生する可能性はありますか? 期待される結果は、100 の異なるクライアントがそれぞれ 1 から 100 までの一意の番号を表示することです。それとも、このようなことが起こるのでしょうか?

  1. クライアント 1 がクエリを実行します。 self.param は 1 ずつ増加します。
  2. return 文が実行される前に、スレッドはクライアント 2 に切り替わります。 self.param が再びインクリメントされます。
  3. スレッドはクライアント 1 に切り替わり、クライアントは番号 2 を返されます、とします。
  4. 今度はスレッドがクライアント 2 に移動し、彼に番号 3 を返します。

クライアントが 2 つしかないので、期待される結果は 1 と 2 であり、2 と 3 ではありません。1 つの番号がスキップされました。

アプリケーションをスケールアップすると、これは実際に起こるのでしょうか? グローバル変数に代わるものとして、どのようなものを検討すべきでしょうか?

どのように解決するのですか?

この種のデータを保持するために、グローバル変数を使用することはできません。スレッドセーフでないだけでなく プロセス 安全ではありません。そして、実稼働中の WSGI サーバは複数のプロセスを生成します。リクエストを処理するためにスレッドを使用していた場合、カウントが間違っているだけでなく、どのプロセスがリクエストを処理するかによって、カウントも変わってきます。

グローバルなデータを保持するために、Flaskの外部のデータソースを使用してください。データベース、memcached、またはredisは、あなたのニーズに応じて、すべて適切な別のストレージ領域です。Pythonのデータをロードしてアクセスする必要がある場合、次のことを検討してください。 multiprocessing.Manager . また、ユーザーごとの簡単なデータにはセッションを使用することもできます。


開発サーバはシングルスレッドとプロセスで実行することができます。各リクエストは同期的に処理されるため、説明したような動作は見られません。スレッドまたはプロセスを有効にすると、それが表示されます。 app.run(threaded=True) または app.run(processes=10) . (1.0では、サーバーはデフォルトでスレッド化されています)。


WSGIサーバの中には、geventや他の非同期ワーカーをサポートするものがあります。グローバル変数は、ほとんどの競合状態に対する保護がないため、まだスレッドセーフではありません。あるワーカーが値を取得し、終了し、別のワーカーがそれを変更し、終了し、最初のワーカーもそれを変更するというシナリオはまだ可能です。


グローバルなデータを保存する必要がある場合 の間に を保存する必要がある場合、Flaskの g オブジェクト . もう一つのよくあるケースは、データベース接続を管理するトップレベルのオブジェクトです。このタイプの "global" の区別は、各リクエストに一意であり、使用されないということです。 の間で使用されないということです。 リクエストの間で使用されないこと、そしてリソースのセットアップとティアダウンを管理するものがあることです。