
目錄
-
鎖的使用姿勢
-
鎖 的作用
-
防止死鎖產生
-
全局鎖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 開銷比較大的情況下,優勢還是特別明顯的。