Engineering · 1 min read
先做完再發佈:安全發佈模式的三步驟實作
當同步原語只提供隔離性、不提供回滾時,原子性得靠手動打造:先在區域變數完成工作,確認成功後才單一寫入共享欄位,避免發佈半成品物件。
先做完再發佈:成功後才指派共享狀態
當同步原語只提供隔離性、不提供回滾時,原子性就需要手動打造。方法濃縮成一句話:把共享欄位的指派,放到最後。
步驟只有三步:
- 把工作做進一個區域變數。
- 確認它成功。
- 然後才把它指派到共享變數 —— 放最後、單一一次寫入。
絕不要發佈一個只初始化一半的物件:別的執行緒可能讀到半成品狀態。這就是「安全發佈(safe publication)」——只有在物件完全建構好時,才讓它的參考可見。
session = requests.Session() # 在區域變數建好
resp = session.post(LOGIN_URL, ...) # 先成功
resp.raise_for_status()
self.session = session # 成功後才發佈,單一寫入
若 post() 拋錯,沒有任何共享狀態被改動 → 下一條執行緒也不會讀到撕裂狀態。
實務上最容易踩的坑,是把順序寫反:self.session = requests.Session() 先擺上去,再去跑可能失敗的登入。這樣一旦登入拋錯,共享欄位早就指向一個沒登入完成的 session 了。
發佈一個參考,等於對其他執行緒宣告「這東西好了」——所以務必等它真的好了。
參考來源:
- https://wiki.sei.cmu.edu/confluence/display/java/TSM03-J.+Do+not+publish+partially+initialized+objects
- 《Java Concurrency in Practice》Ch.3.5 Safe Publication
Related: 參見mutex 與 ACID 隔離性的關係拆解、with lock: 如何保證例外時也釋放鎖,或回到讓 login 執行緒安全的四筆學費總覽。