1. 程式人生 > >Python進階(3)_進程與線程中的lock(互斥鎖、遞歸鎖、信號量)

Python進階(3)_進程與線程中的lock(互斥鎖、遞歸鎖、信號量)

fun 我們 bsp 控制 支持 發生 class 線程 數據操作

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(互斥鎖、遞歸鎖、信號量)