python基礎之多線程鎖機制


GIL(全局解釋器鎖)

GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念,是為了實現不同線程對共享資源訪問的互斥,才引入了GIL

在Cpython解釋器中,同一個進程下開啟的多線程,同一時刻只能有一個線程執行,無法利用多核優勢

python對於計算密集型的任務開多線程的效率甚至不如串行(沒有大量切換),但是,對於IO密集型的任務效率還是有顯著提升的。

GIL原理圖

 

計算密集型:結果肯定是100,因為每一次start結果就已經出來了,所以第二個線程肯定是通過調用第一個線程的count值進行計算的

 1 def sub():
 2     global count
 3 
 4     '''線程的公共數據  下'''
 5     temp=count
 6     count=temp+1
 7     '''線程的公共數據  上'''
 8 
 9     time.sleep(2)
10 count=0
11 
12 l=[]
13 for i in range(100):
14     t=threading.Thread(target=sub,args=())
15     t.start()  #每一次線程激活,申請一次gillock
16     l.append(t)
17 for t in l:
18     t.join()
19 print(count)

 

io密集型:當第一個線程開始start的時候,由於sleep了0.001秒,這0.001秒對於人而言很短,但是對於cpu而言,這0.001秒已經做了很多的事情了,在這里cpu做的事情就是或許已經start了100個線程,所以導致大多數的線程調用的count值還是0,即temp=0,只有少數的線程完成了count=temp+1的操作,所以輸出的count結果不確定,可能是7、8、9,也可能是10幾。

 1 def sub():
 2     global count
 3 
 4     '''線程的公共數據  下'''
 5     temp=count
 6     time.sleep(0.001)    #大量的io操作
 7     count=temp+1
 8     '''線程的公共數據  上'''
 9 
10     time.sleep(2)
11 count=0
12 
13 l=[]
14 for i in range(100):
15     t=threading.Thread(target=sub,args=())
16     t.start()
17     l.append(t)
18 for t in l:
19     t.join()
20 print(count)

 

注意以下的鎖都是多線程提供的鎖機制,與python解釋器引入的gil概念無關

互斥鎖(同步鎖)

互斥鎖是用來解決上述的io密集型場景產生的計算錯誤,即目的是為了保護共享的數據,同一時間只能有一個線程來修改共享的數據。

 1 def sub():
 2     global count
 3     lock.acquire()  #上鎖,第一個線程如果申請到鎖,會在執行公共數據的過程中持續阻塞后續線程
 4                     #即后續第二個或其他線程依次來了發現已經被上鎖,只能等待第一個線程釋放鎖
 5                     #當第一個線程將鎖釋放,后續的線程會進行爭搶
 6 
 7     '''線程的公共數據  下'''
 8     temp=count
 9     time.sleep(0.001)
10     count=temp+1
11     '''線程的公共數據  上'''
12 
13     lock.release()  #釋放鎖
14     time.sleep(2)
15 count=0
16 
17 l=[]
18 lock=threading.Lock()   #將鎖內的代碼串行化
19 for i in range(100):
20     t=threading.Thread(target=sub,args=())
21     t.start()
22     l.append(t)
23 for t in l:
24     t.join()
25 print(count)

 

死鎖

保護不同的數據就應該加不同的鎖。

所以當有多個互斥鎖存在的時候,可能會導致死鎖,死鎖原理如下:

 1 import threading
 2 import time
 3 def foo():
 4     lockA.acquire()
 5     print('func foo ClockA lock')
 6     lockB.acquire()
 7     print('func foo ClockB lock')
 8     lockB.release()
 9     lockA.release()
10 
11 def bar():
12 
13     lockB.acquire()
14     print('func bar ClockB lock')
15     time.sleep(2)  # 模擬io或者其他操作,第一個線程執行到這,在這個時候,lockA會被第二個進程占用
16                    # 所以第一個進程無法進行后續操作,只能等待lockA鎖的釋放
17     lockA.acquire()
18     print('func bar ClockA lock')
19     lockB.release()
20     lockA.release()
21 
22 def run():
23     foo()
24     bar()
25 
26 lockA=threading.Lock()
27 lockB=threading.Lock()
28 for i in range(10):
29     t=threading.Thread(target=run,args=())
30     t.start()
31 
32 輸出結果:只有四行,因為產生了死鎖阻斷了
33 func foo ClockA lock
34 func foo ClockB lock
35 func bar ClockB lock
36 func foo ClockA lock

 

 

遞歸鎖(重要)

解決死鎖

 1 import threading
 2 import time
 3 def foo():
 4     rlock.acquire()
 5     print('func foo ClockA lock')
 6     rlock.acquire()
 7     print('func foo ClockB lock')
 8     rlock.release()
 9     rlock.release()
10 
11 def bar():
12     rlock.acquire()
13     print('func bar ClockB lock')
14     time.sleep(2)
15     rlock.acquire()
16     print('func bar ClockA lock')
17     rlock.release()
18     rlock.release()
19 
20 
21 def run():
22     foo()
23     bar()
24 
25 rlock=threading.RLock() #RLock本身有一個計數器,如果碰到acquire,那么計數器+1
26                         #如果計數器大於0,那么其他線程無法查收,如果碰到release,計數器-1
27 
28 for i in range(10):
29     t=threading.Thread(target=run,args=())
30     t.start()

 

 

Semaphore(信號量)

實際上也是一種鎖,該鎖用於限制線程的並發量

以下代碼在sleep兩秒后會打印出100個ok

1 import threading
2 import time
3 def foo():
4     time.sleep(2)
5     print('ok')
6 
7 for i in range(100):
8     t=threading.Thread(target=foo,args=())
9     t.start()

 

每2秒打印5次ok

 1 import threading
 2 import time
 3 sem=threading.Semaphore(5)
 4 def foo():
 5     sem.acquire()
 6     time.sleep(2)
 7     print('ok')
 8     sem.release()
 9 
10 for i in range(100):
11     t=threading.Thread(target=foo,args=())
12     t.start()


免責聲明!

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



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