Python進階(3)_進程與線程中的lock(互斥鎖、遞歸鎖、信號量)
1、同步鎖 (Lock)
當各個線程需要訪問一個公共資源時,會出現數據紊亂
例如:
1 import threading,time 2 def sub(): 3 global num #對全局變量進行操作 4 5 temp=num 6 time.sleep(0.001) #模擬線程執行中出現I/o延遲等 7 num=temp-1 #所有線程對全局變量進行減一 8 9 time.sleep(1) 10 11 num=100 12 l=[] 13 14 for i in range(100): 15 t=threading.Thread(target=sub,args=())16 t.start() 17 l.append(t) 18 19 for obj in l: 20 obj.join() 21 22 print(num) 23 24 #執行結果不可預期: 25 >>:90 26 >>:93 27 >>:92 28
當全局資源(counter)被搶占的情況,問題產生的原因就是沒有控制多個線程對同一資源的訪問,對數據造成破壞,使得線程運行的結果不可預期。這種現象稱為“線程不安全”。在開發過程中我們必須要避免這種情況,那怎麽避免?這就用到了互斥鎖了。
互斥鎖概念
Python編程中,引入了對象互斥鎖的概念,來保證共享數據操作的完整性。每個對象都對應於一個可稱為” 互斥鎖” 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。在Python中我們使用threading模塊提供的Lock類。
我們對上面的程序進行整改,為此我們需要添加一個互斥鎖變量lock = threading.Lock(),然後在爭奪資源的時候之前我們會先搶占這把鎖lock.acquire(),對資源使用完成之後我們在釋放這把鎖mutex.release()。
代碼如下:
import threading,time def sub(): global num lock.acquire() temp=num time.sleep(0.01) num=temp-1 lock.release() time.sleep(1) num=100 l=[] lock=threading.Lock() for i in range(100): t=threading.Thread(target=sub,args=()) t.start() l.append(t) for obj in l: obj.join() print(num)
2、死鎖與遞歸鎖
所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
會產生死鎖的例子:
class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): self.foo() def foo(self): LockA.acquire() print(‘I am %s GET LOCKA---------%s‘%(self.name,time.ctime())) LockB.acquire() print(‘I am %s GET LOCKB---------%s‘ % (self.name, time.ctime())) LockB.release() LockA.release() LockA=threading.Lock() LockB=threading.Lock() for i in range(10): t=MyThread() t.start()
使用遞歸鎖解決:
在Python中為了支持在同一線程中多次請求同一資源,python提供了可重入鎖RLock。這個RLock內部維護著一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。上面的例子如果使用RLock代替Lock,則不會發生死鎖:
class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): self.foo() self.bar() def foo(self): RLock.acquire() print(‘I am %s GET LOCKA---------%s‘%(self.name,time.ctime())) RLock.acquire() print(‘I am %s GET LOCKB---------%s‘ % (self.name, time.ctime())) RLock.release() RLock.release() def bar(self): RLock.acquire() print(‘I am %s GET LOCKB---------%s‘ % (self.name, time.ctime())) time.sleep(1) RLock.acquire() print(‘I am %s GET LOCKA---------%s‘ % (self.name, time.ctime())) RLock.release() RLock.release() RLock=threading.RLock() for i in range(10): t=MyThread() t.start()
3、Semaphore(信號量)
Semaphore管理一個內置的計數器,
每當調用acquire()時內置計數器-1;
調用release() 時內置計數器+1;
計數器不能小於0;當計數器為0時,acquire()將阻塞線程直到其他線程調用release()。
實例:(同時只有5個線程可以獲得semaphore,即可以限制最大連接數為5):
1 import threading 2 import time 3 4 semaphore = threading.Semaphore(5) 5 6 def func(): 7 if semaphore.acquire(): 8 print (threading.currentThread().getName() + ‘ get semaphore‘) 9 time.sleep(2) 10 semaphore.release() 11 12 for i in range(20): 13 t1 = threading.Thread(target=func) 14 t1.start()
Python進階(3)_進程與線程中的lock(互斥鎖、遞歸鎖、信號量)