120 python程序中的線程操做-鎖

1、同步鎖

1.1 多個線程搶佔資源的狀況

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)

1.2 對公共數據進行鎖操做

import threading
lock = threading.Lock()
lock.acquire() # 鎖頭
'''
公共數據的一系列操做 
'''
lock.replease() # 釋放鎖

1.3 同步鎖的引用

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

2、死鎖與遞歸鎖

所謂死鎖:是指兩個或兩個以上的進程或線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程,以下就是死鎖操作系統

2.1 死鎖實例

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幾回

2.2 遞歸鎖解決上述死鎖問題

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()

# 此時程序將會正常執行結束

3、信號量

關鍵字: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()
相關文章
相關標籤/搜索