我嘗試在多個執行緒中使用相同的變量,但該變數的值並未在執行緒之間一致更新。例如,當執行緒 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
的存取。
問題是,每次更改 query
後,您都會回到 getch
,這會鎖定 gil。當getch
返回時,已經過去了足夠的時間,gil 立即被移交,另一個線程檢查更改,看到最後的更改並報告它,主線程最終得到返回控制權,但通常在getch
再次鎖定它之前沒有做足夠的事情來導致另一次gil 切換,並且另一個線程永遠沒有機會運行並看到更改,直到getch
下次返回。這可能會因 python 版本的不同而有所不同(檢查 gil 的規則會不時發生變化),但它總是不穩定。
正確的解決方案是getch
模組在進行阻塞呼叫之前在內部釋放gil,但如果做不到這一點,您可以透過在每個getch
之前故意以gil 釋放方式阻塞來給其他執行緒一些執行時間呼叫時,透過匯入time
模組,並加入一個sleep 來給其他執行緒時間來查看最新的變更:
while True: time.sleep(0.001) # Explicitly releases GIL for a millisecond char = getch.getch()
這會得到您期望的行為,雖然從技術上講,如果涉及其他線程,則會受到競爭條件的影響,但對於像這樣的兩個線程來說,它是相當可靠的。
以上是為什麼 Python 中一個執行緒對共享變數所做的變更對其他執行緒不可見?的詳細內容。更多資訊請關注PHP中文網其他相關文章!