python 基礎(四)鎖 threading.Lock 死鎖

python基礎系列 正在持續更新中:)

鎖LOCK的實例

首先我們知道線程共用資源(比如下面例子的list1變量就是可以共同訪問)
上一節提到,如果處理不當,線程異步的共同訪問一個變量會造成數據紊亂 所以需要人爲加鎖,這就是Thread.Lock
看看下面的例子是怎麼防止共同訪問的(避免數據紊亂)

#-*- utf-8 -*-
from threading import Lock,Thread
from time import sleep,time
lock = Lock()
list1 = [0]*10

def putR(s):
    lock.acquire()
    for i in range(len(list1)):
        list1[i] = 1
        print("put ",list1[i])
        sleep(s)
    lock.release()

def getR(s):
    lock.acquire()
    for i in range(len(list1)):
        print("get ",list1[i])
        sleep(s)
    lock.release()


if __name__ == "__main__":

    t1 = Thread(target=putR, name="aa", args=(0.5,))

    t2 = Thread(target=getR, name="aa", args=(0.5,))

    t2.start()
    t1.start()


    t1.join()
    t2.join()

結果

get  0
get  0
get  0
get  0
get  0
get  0
get  0
get  0
get  0
get  0
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1

Process finished with exit code 0

這裏start執行順序將會影響線程的執行,爲什麼?
前面沒有鎖的時候我說誰先start都一樣的 start順序沒太大意義
回答:這裏不再是異步被調用,非阻塞的執行,而是同步調用,阻塞執行,誰先start就誰先做事(比如上一例中的get()),另一個只能等着(比如上一例中的put())。類比上廁所搶位置,他先搶到,廁所上鎖(acquire)“有人”,我就只能等着,等他上完,鎖解除(release),我才能進去。

我們來做個實驗驗證一下:
如果將t1 t2 start的順序改變 那麼就會變成:

put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
put  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1
get  1

Process finished with exit code 0

run 與 start

我們簡單複習一下:
start啓動線程之後,調用run具體執行,執行參數包括我們傳過去的target = func 參數。

如果,我們想改造線程執行的內容就更改run重寫)。不更改start是因爲start包含線程之間的很多複雜的執行內容,我們最多可以試着添加新的內容,但是不能重寫。這樣添加新內容還不如更改之前人家已經給好的run呢。

死鎖

這裏我們自定義兩個線程類ThreadA 和 ThreadB
所謂死鎖,主要針對鎖的嵌套問題。
線程都想獲得完一個鎖以後,還需要另一個鎖的保護,但是隻有兩個鎖A B。線程A獲得lockA以後線程B獲得lockB,這時
線程A期待lockB將會release給他
線程B期待lockA將會release給他
於是類似獨木橋,
“你倒是下來啊”
“你咋不上來呢”

大致有個印象,我們來看實例:

#-*- utf-8 -*-
from threading import Thread, Lock
from time import time, sleep

lockA = Lock()
lockB = Lock()


class ThreadA(Thread):
    def run(self):  # 重載 start
        if lockA.acquire():
            print(self.name + ' get Lock A')
            sleep(0.1)
            if lockB.acquire():
                print(self.name + ' get Lock A+B')
                sleep(0.1)
                lockB.release()
            lockA.release()


class ThreadB(Thread):
    def run(self):  # 重載 start
        if lockB.acquire():
            print(self.name + ' get Lock B')
            sleep(0.1)
            if lockA.acquire():
                print(self.name + ' get Lock B+A')
                sleep(0.1)
                lockA.release()
            lockB.release()


if __name__ == '__main__':
    t1 = ThreadA()
    t2 = ThreadB()

    t1.start()
    t2.start()

    t1.join()
    t2.join()

ThreadA先拿到LockA是沒問題的,誰先搶到廁所誰就贏了:)

執行結果:
在這裏插入圖片描述
明顯,ThreadA與ThreadB互不相讓,各自爲政,各拿一個鎖期待另一個人退一步,然後僵持在這裏。
一般解決方法有兩個,一個是設置timeout,將代碼的acquire屬性用起來:
在這裏插入圖片描述
結果如下:
在這裏插入圖片描述
因爲ThreadA先執行,都是timeout=2 於是A先釋放了lockA,B就等到了,拿到了雙殺 雙鎖

再看看另一個例子:

#-*- utf-8 -*-
from threading import Thread, Lock
from time import time, sleep

lockA = Lock()
lockB = Lock()


class ThreadA(Thread):
    def run(self):  # 重載 start
        while True:
            if lockA.acquire(timeout=2):
                print(self.name + ' get Lock A')
                sleep(0.1)
                if lockB.acquire(timeout=2):
                    print(self.name + ' get Lock A+B')
                    sleep(0.1)
                    lockB.release()
                lockA.release()


class ThreadB(Thread):
    def run(self):  # 重載 start
        while True:
            if lockB.acquire(timeout=2):
                print(self.name + ' get Lock B')
                sleep(0.1)
                if lockA.acquire(timeout=2):
                    print(self.name + ' get Lock B+A')
                    sleep(0.1)
                    lockA.release()
                lockB.release()


if __name__ == '__main__':
    t1 = ThreadA()
    t2 = ThreadB()

    t1.start()
    t2.start()

    t1.join()
    t2.join()

結果是這樣的:

Thread-1 get Lock A
Thread-2 get Lock B
Thread-2 get Lock B
Thread-1 get Lock A

Thread-1 get Lock A
Thread-2 get Lock B
Thread-2 get Lock B
Thread-1 get Lock A
Thread-1 get Lock A
Thread-2 get Lock B

思考一下爲什麼會這樣:)

下一站:python 基礎(五)協程 —— 微線程 greenlet gevent

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章