python 多執行緒鎖
python的鎖可以獨立提取出來
1 2 3 4 5 6 7 8 |
|
概念
好幾個人問我給資源加鎖是怎麼回事,其實並不是給資源加鎖, 而是用鎖去鎖定資源,你可以定義多個鎖, 像下面的程式碼, 當你需要獨佔某一資源時,任何一個鎖都可以鎖這個資源
就好比你用不同的鎖都可以把相同的一個門鎖住是一個道理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#只是定義一個鎖,並不是給資源加鎖,你可以定義多個鎖,像下兩行程式碼,當你需要佔用這個資源時,任何一個鎖都可以鎖這個資源
self )
|
執行緒不安全:
最普通的一個多執行緒小例子。我一筆帶過地講一講,我建立了一個繼承Thread類的子類MyThread,作為我們的執行緒啟動類。按照規定,重寫Thread的run方法,我們的執行緒啟動起來後會自動呼叫該方法。於是我首先建立了10個執行緒,並將其加入列表中。再使用一個for迴圈,開啟每個執行緒。在使用一個for迴圈,呼叫join方法等待所有執行緒結束才退出主執行緒。
這段程式碼看似簡單,但實際上隱藏著一個很大的問題,只是在這裡沒有體現出來。你真的以為我建立了10個執行緒,並按順序呼叫了這10個執行緒,每個執行緒為n增加了1.實際上,有可能是A執行緒執行了n++,再C執行緒執行了n++,再B執行緒執行n++。
這裡涉及到一個“鎖”的問題,如果有多個執行緒同時操作一個物件,如果沒有很好地保護該物件,會造成程式結果的不可預期(比如我們在每個執行緒的run方法中加入一個time.sleep(1),並同時輸出執行緒名稱,則我們會發現,輸出會亂七八糟。因為可能我們的一個print語句只打印出一半的字元,這個執行緒就被暫停,執行另一個去了,所以我們看到的結果很亂),這種現象叫做“執行緒不安全”
執行緒鎖:
於是,Threading模組為我們提供了一個類,Threading.Lock,鎖。我們建立一個該類物件,線上程函式執行前,“搶佔”該鎖,執行完成後,“釋放”該鎖,則我們確保了每次只有一個執行緒佔有該鎖。這時候對一個公共的物件進行操作,則不會發生執行緒不安全的現象了。
於是,我們把程式碼更改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
我們看到,我們先建立了一個threading.Lock類物件lock,在run方法裡,我們使用lock.acquire()獲得了這個鎖。此時,其他的執行緒就無法再獲得該鎖了,他們就會阻塞在“if lock.acquire()”這裡,直到鎖被另一個執行緒釋放:lock.release()。
所以,if語句中的內容就是一塊完整的程式碼,不會再存在執行了一半就暫停去執行別的執行緒的情況。所以最後結果是整齊的。
就如同在java中,我們使用synchronized關鍵字修飾一個方法,目的一樣,讓某段程式碼被一個執行緒執行時,不會打斷跳到另一個執行緒中。
這是多執行緒佔用一個公共物件時候的情況。如果多個執行緒要呼叫多個現象,而A執行緒呼叫A鎖佔用了A物件,B執行緒呼叫了B鎖佔用了B物件,A執行緒不能呼叫B物件,B執行緒不能呼叫A物件,於是一直等待。這就造成了執行緒“死鎖”。
Threading模組中,也有一個類,RLock,稱之為可重入鎖。該鎖物件內部維護著一個Lock和一個counter物件。counter物件記錄了acquire的次數,使得資源可以被多次require。最後,當所有RLock被release後,其他執行緒才能獲取資源。在同一個執行緒中,RLock.acquire可以被多次呼叫,利用該特性,可以解決部分死鎖問題。