鎖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
思考一下爲什麼會這樣:)