from threading import Thread,Lock x = 0 def task(): global x for i in range(200000): x = x+1 # t1 的 x剛拿到0 保存狀態 就被切了 # t2 的 x拿到0 進行+1 1 # t1 又得到運行了 x = 0 +1 1 # 這就產生了數據安全問題. if __name__ == '__main__': # 使用的是操做系統的原生線程. t1 = Thread(target=task) t2 = Thread(target=task) t3 = Thread(target=task) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(x)
import threading lock = threading.Lock() lock.acquire() # 鎖頭 ''' 公共數據的一系列操做 ''' lock.replease() # 釋放鎖
from threading import Thread,Lock x = 0 mutex = Lock() def task(): global x mutex.acquire() for i in range(200000): x = x+1 mutex.release() if __name__ == '__main__': # 使用的是操做系統的原生線程. t1 = Thread(target=task) t2 = Thread(target=task) t3 = Thread(target=task) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(x) #結果確定是600000,由原來的併發執行變成串行,犧牲了執行效率保證了數據安全
既然加鎖會讓運行變成串行,那麼我在start以後當即使用join,就不用加鎖了啊,也是串行的效果啊python
沒錯:在start以後馬上使用jion,確定會將100個任務的執行變成串行,毫無疑問,最終n的結果也確定是0,是安全的,但問題是安全
start後當即join:任務內的全部代碼都是串行執行的,而加鎖,只是加鎖的部分即修改共享數據的部分是串行的併發
單從保證數據安全方面,兩者均可以實現,但很明顯是加鎖的效率更高.ui
所謂死鎖:是指兩個或兩個以上的進程或線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程,以下就是死鎖操作系統
from threading import Thread,Lock mutex1 = Lock() mutex2 = Lock() import time class MyThreada(Thread): def run(self): self.task1() self.task2() def task1(self): mutex1.acquire() print(f'{self.name} 搶到了 鎖1 ') mutex2.acquire() print(f'{self.name} 搶到了 鎖2 ') mutex2.release() print(f'{self.name} 釋放了 鎖2 ') mutex1.release() print(f'{self.name} 釋放了 鎖1 ') def task2(self): mutex2.acquire() print(f'{self.name} 搶到了 鎖2 ') time.sleep(1) mutex1.acquire() print(f'{self.name} 搶到了 鎖1 ') # 程序在這裏的時候會卡住 mutex1.release() print(f'{self.name} 釋放了 鎖1 ') mutex2.release() print(f'{self.name} 釋放了 鎖2 ') for i in range(3): t = MyThreada() t.start()
Thread-1 搶到了 鎖1
Thread-1 搶到了 鎖2
Thread-1 釋放了 鎖2
Thread-1 釋放了 鎖1
Thread-1 搶到了 鎖2
Thread-2 搶到了 鎖1線程
**:線程1在sleep1秒後拿到了鎖頭1,可是在線程1sleep的過程當中,操做系統已經去調度了其餘的線程,而另外一個線程開始運行拿到了鎖頭2,而後當線程拿到鎖2時sleep的時候,cpu又調度去執行了線程1,可是線程1這個時候已經釋放鎖1,須要鎖2,可是鎖2此時已經被線程2拿到尚未釋放,而線程2接着往下執行的條件是拿到鎖1,也就是能夠理解爲:code
線程1 拿到鎖1,沒釋放遞歸
線程2 拿到鎖2,沒釋放進程
線程1往下執行的條件是拿到鎖2資源
線程2往下執行的條件是拿到鎖1
這個時候,就出現了資源佔用的問題,程序就會一直卡在那裏,形成了死鎖
解決的放法:遞歸鎖
遞歸鎖,在Python中爲了支持在同一線程中屢次請求同一資源,python提供了可重入鎖RLock。
這個RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源能夠被屢次require。直到一個線程全部的acquire都被release,其餘的線程才能得到資源。上面的例子若是使用RLock代替Lock,則不會發生死鎖。
只有同一個線程下能夠屢次acquire,acquire了幾回就要release幾回
from threading import Thread,Lock,RLock # 遞歸鎖 在同一個線程內能夠被屢次acquire # 如何釋放 內部至關於維護了一個計數器 也就是說同一個線程 acquire了幾回就要release幾回 # mutex1 = Lock() # mutex2 = Lock() mutex1 = RLock() mutex2 = mutex1 import time class MyThreada(Thread): def run(self): self.task1() self.task2() def task1(self): mutex1.acquire() print(f'{self.name} 搶到了 鎖1 ') mutex2.acquire() print(f'{self.name} 搶到了 鎖2 ') mutex2.release() print(f'{self.name} 釋放了 鎖2 ') mutex1.release() print(f'{self.name} 釋放了 鎖1 ') def task2(self): mutex2.acquire() print(f'{self.name} 搶到了 鎖2 ') time.sleep(1) mutex1.acquire() print(f'{self.name} 搶到了 鎖1 ') mutex1.release() print(f'{self.name} 釋放了 鎖1 ') mutex2.release() print(f'{self.name} 釋放了 鎖2 ') for i in range(3): t = MyThreada() t.start() # 此時程序將會正常執行結束
關鍵字:Semaphore
Semaphore也是一個類,須要一個參數,參數是整型,表明能夠同時能夠有多個線程能夠拿到多把鎖
參數是幾就表明一次能夠執行多少個線程去拿幾把鎖
from threading import Thread,currentThread,Semaphore import time def task(): sm.acquire() print(f'{currentThread().name} 在執行') time.sleep(3) sm.release() sm = Semaphore(5) for i in range(15): t = Thread(target=task) t.start()