標籤:time 同步鎖 有一個 共用資料 互斥 global semaphore int cti
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=10012 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 >>:9026 >>:9327 >>:9228
當全域資源(counter)被搶佔的情況,問題產生的原因就是沒有控制多個線程對同一資源的訪問,對資料造成破壞,使得線程啟動並執行結果不可預期。這種現象稱為“線程不安全”。在開發過程中我們必須要避免這種情況,那怎麼避免?這就用到了互斥鎖了。
互斥鎖概念
Python編程中,引入了對象互斥鎖的概念,來保證共用資料操作的完整性。每個對象都對應於一個可稱為” 互斥鎖” 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。在Python中我們使用threading模組提供的Lock類。
我們對上面的程式進行整改,為此我們需要添加一個互斥鎖變數lock = threading.Lock(),然後在爭奪資源的時候之前我們會先搶佔這把鎖lock.acquire(),對資源使用完成之後我們在釋放這把鎖mutex.release()。
代碼如下:
import threading,timedef sub(): global num lock.acquire() temp=num time.sleep(0.01) num=temp-1 lock.release() time.sleep(1)num=100l=[]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(互斥鎖、遞迴鎖、訊號量)