python基礎之多線程鎖機制

GIL(全局解釋器鎖)python

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

在Cpython解釋器中,同一個進程下開啓的多線程,同一時刻只能有一個線程執行,沒法利用多核優點併發

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

GIL原理圖ui

 

計算密集型:結果確定是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結果不肯定,多是七、八、9,也多是10幾。code

 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概念無關blog

互斥鎖(同步鎖)遞歸

互斥鎖是用來解決上述的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()
相關文章
相關標籤/搜索