Skip to content
All writing Part 04 of 04 · [object Object]
Engineering · 1 min read

雙重檢查鎖定:兩個看似相同的檢查各司其職

雙重檢查鎖定寫出兩個看似重複的 if 判斷,其實各自解決不同問題。拿掉任何一個,惰性初始化都會在併發下悄悄壞掉。

雙重檢查鎖定:兩個看似相同的檢查,其實各司其職

若想用最小的鎖成本惰性初始化共享狀態,就會寫出兩個一模一樣的 if。它們看起來重複,其實解決不同問題——拿掉任何一個都會壞。

  • 檢查 1(不加鎖): 熱路徑的快速出口。若值已快取且新鮮,直接回傳、完全不取鎖 → 並發讀取者不需排隊。
  • 檢查 2(持鎖中): 涵蓋「#1 失敗」到「取得鎖」之間的空檔。這段時間另一條執行緒可能已經建好了值——再檢查一次才不會重 build。
def login(self):
    if self._fresh():            # 檢查 1:不加鎖的快速出口
        return self.session
    with self._login_lock:
        if self._fresh():        # 檢查 2:別人可能已經建好了
            return self.session
        self._do_login()         # 真正重建
        return self.session

可以拿掉哪個?

  • 拿掉 #1 →「單重檢查鎖定」:仍正確,只是每次呼叫都取鎖。
  • 拿掉 #2 → 去重失效:兩個 miss 的執行緒都會重建並互相覆蓋。

什麼時候才值得? 只有當熱讀取路徑有競爭、且臨界區成本不小時,DCL 才划算。若每次呼叫本來就被 DB 查詢(~ms)主導,鎖成本(~ns)只是雜訊——這時 DCL 只是慣例寫法,而非效能關鍵,單重檢查更簡單。

重複的程式碼不一定是壞味道——先問它們是不是在解不同的問題。


參考來源:

Related: 參見with lock: 如何保證例外時也釋放鎖,或回到讓 login 執行緒安全的四筆學費總覽

Tags #concurrency #python
// connect

Be brave | Be wise | Be grateful

21 BreakinCode

// elsewhere
LinkedInMedium (lang: en)Youtube
wh:~$William Hung· © 2026 Taipei · GMT+8 · Available for collaboration