同じ変数を複数のスレッドで使用しようとしましたが、変数の値がスレッド間で一貫して更新されませんでした。たとえば、スレッド 1 が変数を 1 に更新すると、スレッド 2 はこの変更を認識せず、古い値を参照します。
これは、問題を説明する簡単なコード例です。ユーザーが「a」キーを押すと、変数「query」が更新され、次のように表示されます。
import getch import threading QUERY = "" EXIT_THREAD = False def input_thread(): global EXIT_THREAD last_query = "" while not EXIT_THREAD: if last_query != QUERY: last_query = QUERY print(f"Query: {QUERY}") thread = threading.Thread(target=input_thread) thread.start() while True: char = getch.getch() if char == "\n": break elif char == "\x7f": QUERY = QUERY[:-1] else: QUERY += char print(f"Query1: {QUERY}") # kill input thread EXIT_THREAD = True thread.join()
getchモジュールのコーディングが不十分であることです。入力の待機をブロックしている間、
gil (グローバル インタプリタ ロック) は解放されないため、他のスレッド は実行できません。ロックとの同期はあまり役に立ちません。gil は query へのアクセスを保護しています。
問題は、
を変更するたびに、ギルをロックするgetch
に戻ることです。 getch
が返されると、ギルがすぐに引き渡されるのに十分な時間が経過し、別のスレッドが変更をチェックし、最後の変更を確認して報告し、最終的にメインスレッドが制御を取り戻しますが、通常は getch
再度ロックするまでに十分な処理が行われていないため、別の gil スイッチが発生し、次回 getch
が返されるまで、別のスレッドが実行されて変更を確認する機会が得られません。これは Python のバージョンによって異なる場合があります (Gil をチェックするルールは時々変更されます) が、常に不安定です。
正しい解決策は、ブロッキング呼び出しを行う前に
モジュールが内部で gil を解放することですが、それができない場合は、gil release を呼び出すときに、各 getch
に意図的にプレフィックスを付けることでこれを行うことができます。モードをブロックして他のスレッドに実行時間を与え、time
モジュールをインポートし、スリープを追加して他のスレッドに最新の変更を表示する時間を与えます:
リーリー
これにより、期待どおりの動作が得られます。ただし、技術的には、
スレッドが関与している場合は競合状態が発生しますが、このような 2 つのスレッドの場合はかなり信頼性があります。
以上がPython では、あるスレッドによって共有変数に加えられた変更が他のスレッドから見えないのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。