並發編程——全局解釋器鎖GIL
阿新 • • 發佈:2019-05-08
rand 安全 能夠 互斥 star list lis 解釋器 semaphore
-
-
線程是執行單位,但是不能直接運行,需要先拿到python解釋器解釋之後才能被cpu執行
-
同一時刻同一個進程內多個線程無法實現並行,但是可以實現並發
-
為什麽要有GIL是因為它內部的垃圾回收機制不是線程安全的
-
垃圾回收機制也是一個任務,跟你的代碼不是串行運行,如果是串行會明顯有卡頓
-
這個垃圾回收到底是開進程還是開線程?肯定是線程,線程肯定也是一段代碼,所以想運行也必須要拿到python解釋器
-
假設能夠並行,會出現什麽情況?一個線程剛好要造一個a=1的綁定關系之前,這個垃圾線程來掃描,矛盾點就來了,誰成功都不對!
-
也就意味著在Cpython解釋器上有一把GIL全局解釋器鎖
-
同一個進程下的多個線程不能實現並行但是能夠實現並發,多個進程下的線程能夠實現並行
1.python中的多線程到底有沒有用?
單核情況下:四個任務
多核情況下:四個任務
計算密集型:一個任務算十秒,四個進程和四個線程,肯定是進程快
IO密集型:任務都是純io情況下,線程開銷比進程小,肯定是線程好
```python # 計算密集型 from multiprocessing import Process from threading import Thread import os,timeView Codedef work(): res=0 for i in range(100000000): res*=i if __name__ == ‘__main__‘: l=[] print(os.cpu_count()) # 本機為12核 start=time.time() for i in range(12): # p=Process(target=work) #耗時8s多 p=Thread(target=work) #耗時44s多 l.append(p) p.start()for p in l: p.join() stop=time.time() print(‘run time is %s‘ %(stop-start)) # IO密集型 from multiprocessing import Process from threading import Thread import threading import os,time def work(): time.sleep(2) if __name__ == ‘__main__‘: l=[] print(os.cpu_count()) #本機為12核 start=time.time() for i in range(400): p=Process(target=work) #耗時12s多,大部分時間耗費在創建進程上 # p=Thread(target=work) #耗時2s多 l.append(p) p.start() for p in l: p.join() stop=time.time() print(‘run time is %s‘ %(stop-start))
2.GIL與自定義互斥鎖
不同的數據需要加不同的鎖才能保證數據的安全,GIL鎖只是對線程加鎖,對數據並沒有加鎖的效果 ```python from threading import Thread,Lock import time mutex=Lock() n=100 def task(): global n with mutex: temp=n time.sleep(0.1) n=temp-1 if __name__ == ‘__main__‘: l=[] for i in range(100): t=Thread(target=task) l.append(t) t.start() for t in l: t.join() print(n) # 對於修改不同的數據,需要加不同的鎖進行處理View Code
3.死鎖與遞歸鎖(了解)
自定義鎖一次acquire必須對應一次release,不能連續acquire
遞歸鎖可以連續的acquire,每acquire一次計數加一
from threading import Thread,Lock,RLock import time # mutexA=Lock() # mutexB=Lock() mutexB=mutexA=RLock() class Mythead(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print(‘%s 搶到A鎖‘ %self.name) mutexB.acquire() print(‘%s 搶到B鎖‘ %self.name) mutexB.release() mutexA.release() def f2(self): mutexB.acquire() print(‘%s 搶到了B鎖‘ %self.name) time.sleep(2) mutexA.acquire() print(‘%s 搶到了A鎖‘ %self.name) mutexA.release() mutexB.release() if __name__ == ‘__main__‘: for i in range(100): t=Mythead() t.start()View Code
4.信號量(了解)
自定義的互斥鎖如果是一個廁所,那麽信號量就相當於公共廁所,門口掛著多個廁所的鑰匙。搶和釋放跟互斥鎖一致
from threading import Thread,Semaphore import time import random sm = Semaphore(5) # 公共廁所裏面有五個坑位,在廁所外面放了五把鑰匙 def task(name): sm.acquire() print(‘%s正在蹲坑‘%name) # 模擬蹲坑耗時 time.sleep(random.randint(1,5)) sm.release() if __name__ == ‘__main__‘: for i in range(20): t = Thread(target=task,args=(‘傘兵%s號‘%i,)) t.start()View Code
5.Event事件
一些線程需要等待另外一些線程運行完畢才能運行,類似於發射信號一樣
from threading import Thread,Event import time event = Event() # 造了一個紅綠燈 def light(): print(‘紅燈亮著的‘) time.sleep(3) print(‘綠燈亮了‘) event.set() def car(name): print(‘%s 車正在等紅燈‘%name) event.wait() print(‘%s 車加油門飆車走了‘%name) if __name__ == ‘__main__‘: t = Thread(target=light) t.start() for i in range(10): t = Thread(target=car,args=(‘%s‘%i,)) t.start()View Code
6.線程queue
同一個進程下的線程數據都是共享的為什麽還要用queue?queue本身自帶鎖的功能,能夠保證數據的安全
# 我們現在的q只能在本地使用,後面我們會學基於網絡的q import queue queue.Queue() #先進先出 q=queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) queue.LifoQueue() #後進先出->堆棧 q=queue.LifoQueue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) queue.PriorityQueue() #優先級 q=queue.PriorityQueue(3) #優先級,優先級用數字表示,數字越小優先級越高 q.put((10,‘a‘)) q.put((-1,‘b‘)) q.put((100,‘c‘)) print(q.get()) print(q.get()) print(q.get())View Code
並發編程——全局解釋器鎖GIL