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()