互斥鎖、死鎖和遞歸鎖


一、互斥鎖(Mutex)

   在上節最后我們講到了線程安全,線程同步能夠保證多個線程安全訪問競爭資源,最簡單的同步機制是引入互斥鎖。互斥鎖為資源引入一個狀態:鎖定/非鎖定。某個線程要更改共享數據時,先將其鎖定,此時資源的狀態為“鎖定”,其他線程不能更改;直到該線程釋放資源,將資源的狀態變成“非鎖定”,其他的線程才能再次鎖定該資源。互斥鎖保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數據的正確性。

  threading模塊中定義了Lock類,可以方便的處理鎖定: 

#創建鎖
lock = threading.Lock()
#鎖定
lock.acquire(blocking=True, timeout=-1) 
#釋放 
lock.release() 

  還是以上節實例為例:

# -*- coding: UTF-8 -*-

import threading


class MyThread(threading.Thread):

    def run(self):
        global n
        lock.acquire()
        n += 1
        lock.release()
        print(self.name + ' set n to ' + str(n))

n = 0
lock = threading.Lock()

if __name__ == '__main__':

    for i in range(5):
        t = MyThread()
        t.start()

    print('final num: %d' % n)

  輸出:

Thread-1 set n to 1
Thread-2 set n to 2
Thread-3 set n to 3
Thread-4 set n to 4
Thread-5 set n to 5
final num: 5

  加鎖之后,我們可以確保數據的正確性

二、死鎖

  死鎖是指一個資源被多次調用,而多次調用方都未能釋放該資源就會造成一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖。

  2.1 一個線程內部多次加鎖卻沒有釋放  

import threading


class MyThread(threading.Thread):

    def run(self):
        global n1, n2
        lock.acquire()   # 加鎖
        n1 += 1
        print(self.name + ' set n1 to ' + str(n1))
        lock.acquire()   # 再次加鎖
        n2 += n1
        print(self.name + ' set n2 to ' + str(n2))
        lock.release()
        lock.release()

n1, n2 = 0, 0
lock = threading.Lock()

if __name__ == '__main__':
    thread_list = []
    for i in range(5):
        t = MyThread()
        t.start()
        thread_list.append(t)
    for t in thread_list:
        t.join()
    print('final num:%d ,%d' % (n1, n2))

  結果:

Thread-1 set n1 to 1

# 會一直等待

  2.2 多個程序間相互調用引起死鎖

import threading,time
 
def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num +=1
    lock.release()
    return num
def run2():
    print("grab the second part data")
    lock.acquire()
    global  num2
    num2+=1
    lock.release()
    return num2
def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res,res2)
 
 
if __name__ == '__main__':
 
    num,num2 = 0,0
    lock = threading.Lock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
 
while threading.active_count() != 1:
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num,num2) 

 三、遞歸鎖

  上述兩個問題都可以使用python的遞歸鎖解決 

# 遞歸鎖
rlock = threading.RLOCK()

  RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。這里以例1為例,如果使用RLock代替Lock,則不會發生死鎖:

  

# -*- coding: UTF-8 -*-
import threading


class MyThread(threading.Thread):

    def run(self):
        global n1, n2
        lock.acquire()   # 加鎖
        n1 += 1
        print(self.name + ' set n1 to ' + str(n1))
        lock.acquire()   # 再次加鎖
        n2 += n1
        print(self.name + ' set n2 to ' + str(n2))
        lock.release()
        lock.release()

n1, n2 = 0, 0
lock = threading.RLock()

if __name__ == '__main__':
    thread_list = []
    for i in range(5):
        t = MyThread()
        t.start()
        thread_list.append(t)
    for t in thread_list:
        t.join()
    print('final num:%d ,%d' % (n1, n2))

  輸出:

Thread-1 set n1 to 1
Thread-1 set n2 to 1
Thread-2 set n1 to 2
Thread-2 set n2 to 3
Thread-3 set n1 to 3
Thread-3 set n2 to 6
Thread-4 set n1 to 4
Thread-4 set n2 to 10
Thread-5 set n1 to 5
Thread-5 set n2 to 15
final num:5 ,15

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM