首先了解這兩者是什么。
以下說明參考自python官網
Lock:Lock被稱為①原始鎖,原始鎖是一個②在鎖定時不屬於特定線程的同步基元組件,它是能用的最低級的同步基元組件。原始鎖處於 "鎖定" 或者 "非鎖定" 兩種狀態之一。它被創建時為非鎖定狀態。它有兩個基本方法, acquire()
和 release()
。當狀態為非鎖定時, acquire()
將狀態改為鎖定並立即返回。當狀態是鎖定時, acquire()
將阻塞至其他線程調用 release()
將其改為非鎖定狀態,然后 acquire()
調用重置其為鎖定狀態並返回。 release()
只在鎖定狀態下調用; 它將狀態改為非鎖定並立即返回。如果嘗試釋放一個非鎖定的鎖,則會引發 RuntimeError
異常。鎖支持 上下文管理協議,即支持with語句,下文例子中會用到。
RLock:RLock被稱為重入鎖,若要鎖定鎖,線程調用其 acquire()
方法;一旦線程擁有了鎖,方法將返回。若要解鎖,線程調用 release()
方法。 ③acquire()
/release()
對可以嵌套,重入鎖必須由獲取它的線程釋放。一旦線程獲得了重入鎖,同一個線程再次獲取它將不阻塞。只有最終 release()
(最外面一對的 release()
) 將鎖解開,才能讓其他線程繼續處理 acquire()
阻塞。;線程必須在每次獲取它時釋放一次。
兩者使用的方法大部分還是相同的,下面根據以上紅色強調部分描述一下二者的區別
①是名稱的區別,一個叫原始鎖,一個叫重入鎖,這沒啥好說的
②Lock在鎖定時不屬於特定線程,也就是說,Lock可以在一個線程中上鎖,在另一個線程中解鎖。而對於RLock來說,只有當前線程才能釋放本線程上的鎖,即解鈴還須系鈴人:
import threading import time lock = threading.Lock() lock.acquire() def func(): lock.release() print("lock is released") t = threading.Thread(target=func) t.start()
輸出結果為:lock is released
上面代碼中,在主線程中創建鎖,並上鎖,但是是在t線程中釋放鎖,結果正常輸出,說明一個線程上的鎖,可以由另外線程解鎖。如果把上面的鎖改為RLock則報錯
③也是這兩者最大的區別了,RLock允許在同一線程中被多次acquire。而Lock卻不允許這種情況。也就是說,下面的情況對於RLock是允許的:
import threading rlock = threading.RLock() def func(): if rlock.acquire(): # 第一把鎖 print("first lock") if rlock.acquire(): # 第一把鎖沒解開的情況下接着上第二把鎖 print("second lock") rlock.release() # 解開第二把鎖 rlock.release() # 解開第一把鎖 t = threading.Thread(target=func) t.start()
輸出結果:
first lock
second lock
注意上面強調的同一線程中,因為對於RLock來說只有當前線程才能釋放本線程上的鎖,並不能在t1線程中已經執行rlock.acquire,且未釋放鎖的情況下,在另一個t2線程中還能執行rlock.acquire(這種情況會導致t2阻塞)
那么,既然一個線程可以通過Lock來獲得一把鎖,干嘛還要使用RLock去鎖上加鎖?考慮一下這種情況:
import threading import time lock1 = threading.RLock() def inner(): with lock1: print("inner1 function:%s" % threading.current_thread()) def outer(): print("outer function:%s" % threading.current_thread()) with lock1: inner() if __name__ == "__main__": t1 = threading.Thread(target=outer) t2 = threading.Thread(target=outer) t1.start() t2.start()
結果:
outer function:<Thread(Thread-1, started 12892)>
inner1 function:<Thread(Thread-1, started 12892)>
outer function:<Thread(Thread-2, started 7456)>
inner1 function:<Thread(Thread-2, started 7456)>
首先只看t1線程,當執行到outer函數時,首先打印outer function:<Thread(Thread-1, started 12892)>,然后用lock1給當前線程上了一把鎖,然后執行inner函數,在inner里面又需要用lock1來上一把鎖,如果此時用Lock的話,由於已經上了一把鎖,程序會因為第二次無法獲得鎖而導致t1阻塞,程序阻塞之后又沒法釋放鎖,所以會導致程序死鎖。這種情況下,RLock就派上用場了。t2線程執行過程和t1一樣,這里只是多加個線程看起來酷炫一些