為什麼 Python 中一個執行緒對共享變數所做的變更對其他執行緒不可見?

WBOY
發布: 2024-02-06 11:03:11
轉載
1179 人瀏覽過

为什么 Python 中一个线程对共享变量所做的更改对其他线程不可见?

問題內容

我嘗試在多個執行緒中使用相同的變量,但該變數的值並未在執行緒之間一致更新。例如,當執行緒 1 將變數更新為 1 時,執行緒 2 無法識別此更改,而是看到舊值。

這是一個說明問題的簡單程式碼範例。當使用者按下“a”鍵時,變數“query”應更新並顯示如下:

  1. 查詢1:a
  2. 查詢:a

但是,我得到的實際輸出只是:

  1. 查詢1:a

您能幫我理解為什麼會發生這種情況以及如何解決它嗎?

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中文網其他相關文章!

來源:stackoverflow.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!