線程鎖
大家已經(jīng)知道了同一個進(jìn)程下的線程數(shù)據(jù)之間可以共享荒辕,也知道多線程中有GIL鎖汗销,一個時刻只有一線程在運行,所以說就是有很多線程在修改這些共享數(shù)據(jù)抵窒,那么不是同時運行的話修改數(shù)據(jù)會不會出現(xiàn)錯誤呢弛针?在Python2中就會出現(xiàn)這種情況,當(dāng)你開啟了很多條線程李皇,然后這些線程一起修改全局變量時削茁,最后得出的結(jié)果可能跟期望的不太一樣
在這個時候Python就提供了另一把鎖,給用戶的鎖掉房,叫做線程鎖茧跋,可以在多個線程操作共享數(shù)據(jù)時更加有規(guī)律,來防止操作數(shù)據(jù)失誤的情況出現(xiàn)卓囚,下面我們就了解一下如何使用線程鎖
import threading
def run():
# 獲取鎖
lock.acquire()
# 聲明全局變量num
global num
# num+=1
num += 1
# 釋放鎖
lock.release()
# 生成線程鎖實例
lock = threading.Lock()
num = 0
# 開啟1000個線程
for i in range(1000):
t = threading.Thread(target=run)
t.start()
print("-----all thread has finshed")
print("num:", num)
這里要注意的事獲取鎖與釋放鎖之間的這一段所操作的數(shù)據(jù)量不是很大瘾杭,如果數(shù)據(jù)量很大,需要的時間很多哪亿,那么程序就會變成串行
在Python3中這種情況已經(jīng)不會出現(xiàn)了富寿,但是在2中的這種情況與處理方法還是要了解,并且锣夹,在操作共享數(shù)據(jù)時页徐,不管Python2還是Python3,都要加上線程鎖银萍,這是最好的做法
遞歸鎖
接下來我們?nèi)チ私饬硪环N情況变勇,就是鎖中鎖(遞歸鎖),我們分出一個線程贴唇,使用線程鎖之后里面再調(diào)用別的函數(shù)搀绣,然后調(diào)用的這個函數(shù)中再使用線程鎖的話就不能再使用Lock()實例了,不然就會出現(xiàn)死循環(huán)錯誤戳气,這是因為一把鎖對應(yīng)一個鑰匙链患,鎖里面再加一把鎖就會導(dǎo)致程序分不清哪把鑰匙開哪把鎖,導(dǎo)致程序一直在鎖中出不來瓶您,如下
import threading
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()
# 去跑run1
res = run1()
print('--------between run1 and run2-----')
# 去跑run2
res2 = run2()
# 釋放鎖
lock.release()
print(res, res2)
if __name__ == '__main__':
# 初始化兩個為0的變量
num, num2 = 0, 0
# 生成lock實例
lock = threading.Lock()
# 開始10個線程
for i in range(10):
# 線程跑的是run3
t = threading.Thread(target=run3)
t.start()
# 判讀是否有多個線程麻捻,有多個就繼續(xù)打印纲仍,只剩一個說明子線程都執(zhí)行完了,只剩主線程了贸毕,然后跳出循環(huán)程序結(jié)束
# 可以用之前學(xué)到的join()方法來實現(xiàn)一樣的效果
while threading.active_count() != 1:
print(threading.active_count())
else:
print('----all threads done---')
print(num, num2)
可以看到郑叠,一直打印線程數(shù)11,說明一直有11個線程在活躍明棍,說明分出的10個線程一直在運行不結(jié)束乡革,這個時候就不能使用Lock()了,而是使用RLock()摊腋,讓我們修改下再來運行試試
看來沸版,只要將Lock改為RLock就可以解決這種鎖中鎖(遞歸鎖)的情況了
信號量
信號量的用法跟線程鎖非常的相似,其實其中的原理與線程鎖并沒有多大的區(qū)別兴蒸,只不過線程鎖鎖住一個線程在運行和修改數(shù)據(jù)视粮,而信號量可以自己控制同一時刻運行幾個線程和幾個線程修改數(shù)據(jù),也就是設(shè)置最大同時運行的線程數(shù)
import threading
import time
def run(n):
# 獲取信號量
semaphore.acquire()
print('task %s is running' % n)
# 暫停1s方便看出一次運行幾個線程
time.sleep(1)
# 釋放信號量
semaphore.release()
if __name__ == '__main__':
# 生成信號量實例并設(shè)置信號量為5
semaphore = threading.BoundedSemaphore(5)
# 開啟50個線程
for i in range(50):
t = threading.Thread(target=run, args=(i,))
t.start()
# 線程沒有運行完就不退出
while threading.active_count() != 1:
pass
else:
print('----all threads done---')
這里可以自己運行一下类咧,很容易可以看出一次運行五個線程
雖然我們看到的是一次執(zhí)行五個線程馒铃,但并不是五個一組五個一組分組執(zhí)行的蟹腾,因為這五個線程同時完成痕惋,所以我們看不出來,但是其中的過程是執(zhí)行完一個線程放進(jìn)去一個線程娃殖,假如這五個中有兩個先完成值戳,那么就會立刻再放進(jìn)去兩個,也就是說這五個線程之間不會互相等待炉爆,這個設(shè)置的信號量5不是按5來分組堕虹,而是同時運行的線程最大數(shù),可以寫多個執(zhí)行時間不同的函數(shù)然后一次執(zhí)行幾個來證明這一點
轉(zhuǎn)載請注明出處
python自學(xué)技術(shù)互助扣扣群:670402334