Python 線程 的 鎖


 
 
目錄

  • 鎖的使用姿勢
  • 鎖 的作用
  • 防止死鎖產生
  • 全局鎖GIL
 
 
鎖的使用姿勢

姿勢一:
  • threading.Lock()  :  來創建鎖對象
  • acquire()  :獲取鎖
  • release()  :   釋放鎖
 
import threading
 
#創建鎖對象
lock = threading.Lock()
 
#獲取鎖
lock.acquire()
 
#釋放鎖
lock.release()

 

解釋: acquire()  和  release()  是成對出現的。往往死鎖的出現就是  release 沒有執行
 
 
 
姿勢二:
  • threading.Loc() : 創建鎖對象
  • with :  上下文管理器來獲取,釋放鎖
 
import  threading
 
lock = threading.Lock()
 
with lock:
    #業務代碼
    pass
 
解釋: with  是可以 自動 獲取鎖 和 釋放鎖的,可以防止我們忘記釋放鎖而造成死鎖的情況發生
 
 
鎖的作用

    鎖的 核心作用 就是為了保證數據的  一致性 ,對鎖內的資源(變量)進行鎖定,避免其它線程偷偷進行  篡改 。以達到我們的預期效果。即:  異步變同步
 
用例一:
 
 
解析:
    由於線程是內核級別的,它的切換是由CPU說了算,兩線程間的執行順序是完全沒有約束的,輪到誰了就誰上。所以整個結果是無序的。
 
用例二:
 
解析:
  • work1 由於先啟動,它先鎖定了資源
  • work2 切換進來的時候,發現資源是鎖定的,所以它只能繼續等待
  • work2 會在  “切換”與“等待”這兩個狀態間進行輪替,直到work1 釋放鎖
 
所以,我們看到的執行順序一直就是圖上的結果了。這樣也就達到了我們的預期效果了。
 
 
防止死鎖產生

    死鎖的原因是多種多樣的,但是本質就是對資源不合理的競爭鎖導致的。
死鎖的常見原因:
  • 同一個線程  : 嵌套獲取同一把鎖,導致獲取釋放鎖不合理而造成死鎖
  • 多個線程     :    不安順序同事獲取多個鎖,造成死鎖
 
第一種就不用說了,這里介紹一下第二種情況:
前提條件
  • 線程1 : 嵌套獲取  A  , B  兩個鎖
  • 線程2 :   嵌套獲取  B , A 兩個鎖
     
執行情況
  • 線程1 獲取了A  鎖 , 而沒有獲取B 鎖
  • 線程2  獲取了B  鎖 , 而沒有獲取A  鎖
 
執行結果
  • 線程1  在等待線程2 釋放B 鎖 然后結束任務,
  • 線程2 在等待  線程1  釋放A 鎖, 然后結束任務
  • 最終兩個線程就陷入了相互等待的局面了。這個就是死鎖
    
    
 
用例一:
    
 
 
全局鎖

    我們都知道 多進程是真正的並行執行 , 多線程只是交替執行。
    python 中導致 線程 交替執行的是一個叫  GIL 【Global Interpreter Lock】全局鎖 的東西
    
    什么GIL?
    
    Python 在線程執行前,必須加上一個  GIL  鎖, 然后,每執行100條字節碼, 解釋器就釋放GIL 鎖, 讓別的線程有機會執行。這個GIL 全局鎖實際上把  所有 線程的執行代碼都上鎖了,所以,多線程在Python 中只能交替執行 , 即使是100 個線程跑在了 100核的CPU 上,也只有一個核在運算。
    
    注意,GIL 不是Python 本身的特性,而是它的解析器之一的  CPython 的特性。 Python 的解析器還有: PyPy , Psyco , JPython 等。只是我們絕大多數情況下,都是使用的是CPython 這個解析器,所以也就默認了 Python 有  GIL 這個特性了。 
 
    都知道GIL 影響性能, 那么如何避免受到  GIL 的影響?
  •  使用多進程代替多線程
  •  更換Python 解析器, 不使用  CPython   
 
 
另外, Python  的線程在I/O 開銷比較大的情況下,優勢還是特別明顯的。
 
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM