GIL介紹python
GIL本質就是一把互斥鎖,既然是互斥鎖,全部互斥鎖的本質都同樣,都是將併發運行變成串行,以此來控制同一時間內共享數據只能被一個任務所修改,進而保證數據安全。每一個進程內都會存在一把GIL,同一個進程內的多個線程必須搶到GIL以後才能使用Cpython解釋器來執行本身的代碼,即同一個進程下的多個線程沒法實現並行,可是能夠實現併發安全
在Cpython解釋器下,若是想實現並行能夠開啓多個進程多線程
爲什麼要有GIL併發
由於Cpython解釋器的垃圾回收機制不是線程安全的app
總結:dom
io密集型:用多線程(開發中大部分用多線程)ide
計算密集型:用多進程ui
GIL與自定義互斥鎖的區別spa
前者是解釋器級別的(固然保護的就是解釋器級別的數據,好比垃圾回收的數據),後者是保護用戶本身開發的應用程序的數據,很明顯GIL不負責這件事,只能用戶自定義加鎖處理,即Lock線程
鎖一般被用來實現對共享資源的同步訪問。爲每個共享資源建立一個Lock對象,當你須要訪問該資源時,調用acquire方法來獲取鎖對象(若是其它線程已經得到了該鎖,則當前線程需等待其被釋放),待資源訪問完後,再調用release方法釋放鎖:
from threading import Thread,Lock import os,time def work(): global n lock.acquire() temp=n time.sleep(0.1) n=temp-1 lock.release() if __name__ == '__main__': lock=Lock() n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() print(n) #結果確定爲0,由原來的併發執行變成串行,犧牲了執行效率保證了數據安全
死鎖與遞歸鎖
死鎖現象:指的是互相拿了對方須要的鑰匙但都不放手,致使了程序的阻塞
遞歸鎖:就是爲了解決死鎖現象:RLOOK
from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('\033[41m%s 拿到A鎖\033[0m' %self.name) mutexB.acquire() print('\033[42m%s 拿到B鎖\033[0m' %self.name) mutexB.release() mutexA.release() def func2(self): mutexB.acquire() print('\033[43m%s 拿到B鎖\033[0m' %self.name) time.sleep(2) mutexA.acquire() print('\033[44m%s 拿到A鎖\033[0m' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() ''' Thread-1 拿到A鎖 Thread-1 拿到B鎖 Thread-1 拿到B鎖 Thread-2 拿到A鎖 而後就卡住,死鎖了 '''
信號量
semaphore 控制統一進程下的併發的線程個數
from threading import Thread,Semaphore import time,random sm=Semaphore(5) def task(name): sm.acquire() print('%s正在上廁所'%name) time.sleep(random.randint(1,3)) sm.release() if __name__ == '__main__': for i in range(20): t=Thread(target=task,args=('路人%s'%i,)) t.start()
Event
線程的一個關鍵特性是每一個線程都是獨立運行且狀態不可預測。若是程序中的其 他線程須要經過判斷某個線程的狀態來肯定本身下一步的操做,這時線程同步問題就會變得很是棘手。爲了解決這些問題,咱們須要使用threading庫中的Event對象。 對象包含一個可由線程設置的信號標誌,它容許線程等待某些事件的發生。在 初始狀況下,Event對象中的信號標誌被設置爲假。若是有線程等待一個Event對象, 而這個Event對象的標誌爲假,那麼這個線程將會被一直阻塞直至該標誌爲真。一個線程若是將一個Event對象的信號標誌設置爲真,它將喚醒全部等待這個Event對象的線程。若是一個線程等待一個已經被設置爲真的Event對象,那麼它將忽略這個事件, 繼續執行
from threading import Thread,Event import time event=Event() def light(): print('紅燈正亮着') time.sleep(3) event.set()#綠燈亮 def car(name): print('車%s正在等綠燈'%name) event.wait()#等燈綠 print('車%s通行'%name) if __name__ == '__main__': #紅綠燈 t1=Thread(target=light) t1.start() #車 for i in range(10): t=Thread(target=car,args=(i,)) t.start()