''' 定義: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) '''
結論:在Cpython解釋器中,同一個進程下開啓的多線程,同一時刻只能有一個線程執行,沒法利用多核優點
前言: python
- GIL其實就是一把互斥鎖(犧牲了效率可是保證了數據的安全)。 - 線程是執行單位,可是不能直接運行,須要先拿到python解釋器解釋以後才能被cpu執行 - 同一時刻同一個進程內多個線程沒法實現並行,可是能夠實現併發
一.GIL全局解釋器 垃圾回收機制:安全
- 垃圾回收機制也是一個任務,跟你的代碼不是串行運行,若是是串行會明顯有卡頓 - 這個垃圾回收究竟是開進程仍是開線程?確定是線程,線程確定也是一段代碼,因此想運行也必需要拿到python解釋器 沒有GIL全局解釋器鎖 他只是對線程加鎖 不是對數據 運行垃圾回收機制:引用計數 1,必須先拿到python 解釋器---> 2.python 進程下的多個線程是併發。若此時你想建立一個 a = 1 cpu運行速度是很是快的
那麼就會引起 其餘線程垃圾回收機制掃描把我剛建立的內存清理掉 因此必須設置GIL全局解釋器鎖
也就意味着在Cpython解釋器上有一把GIL全局解釋器鎖
二. 多線程
1.python中的多線程到底有沒有用?
1、數據密集型
2、IO密集型
#### 1.python中的多線程到底有沒有用? 單核狀況下:四個任務 多核狀況下:四個任務 計算密集型:一個任務算十秒,四個進程和四個線程,確定是進程快 IO密集型:任務都是純io狀況下,線程開銷比進程小,確定是線程好
1、數據密集型
def task(): res = 0 for i in range(100000000): res = res*i if __name__ == '__main__': print(os.cpu_count()) #本機內核 p_list=[] start_time= time.time() for i in range(4): p = Process(target=task) # 進程運行時間爲10.636553287506104 # p = Thread(target= task) # 線程運行時間爲19.97660756111145 p.start() p_list.append(p) for p in p_list: p.join() end_time = time.time() print('運行時間爲%s'% (end_time-start_time))
"" 2、IO密集型 def work(): time.sleep(3) if __name__ == '__main__': print(os.cpu_count()) start_time =time.time() p_list=[] for i in range(4): # p = Process(target= work) # run is total_time7.271259546279907 p = Thread(target= work) # run is total_time3.002392053604126 p.start() p_list.append(p) for p in p_list: p.join() end_time =time.time() print('run is total_time%s'%(end_time-start_time))
3、全局鎖與普通鎖併發
對於不一樣的數據,要想保證安全,須要加不一樣的鎖處理 GIL並不能保證數據的安全,它是對Cpython解釋器加鎖,針對的是線程 保證的是同一個進程下多個線程之間的安全
""" from threading import Thread import os import time from threading import Lock mutex = Lock() num = 100 def task(): global num mutex.acquire() #搶鎖 temp = num time.sleep(0.1) num = temp-1 mutex.release() # 釋放鎖 開始一個 if __name__ == '__main__': p_lsit=[] for i in range(10): p = Thread(target=task) p.start() p_lsit.append(p) for p in p_lsit: p.join() print(num) # 90 至關於10個線程同時去搶100票 必需要確保一個數據同時被10個進程同時搶 鎖是起到保護做用 取完一個減一個
4、.死鎖與遞歸鎖(瞭解)app
自定義鎖一次acquire必須對應一次release,不能連續acquiredom
遞歸鎖能夠連續的acquire,每acquire一次計數加一ui
import time from threading import Thread,RLock mutexA = mutexB= RLock() # 遞歸鎖RLock class Mythread(Thread): def run(self): self.fn1() self.fn2() def fn1(self): #設置鎖 mutexA.acquire() print('%s 搶到A鎖了'%self.name) mutexB.acquire() print('%s 搶到B鎖了'%self.name) mutexB.release() print('%s釋放了B鎖'%self.name) mutexA.release() print('%s釋放了A鎖'%self.name) def fn2(self): mutexB.acquire() print('%s 搶到A鎖了' % self.name) time.sleep(1) mutexA.acquire() print('%s 搶到B鎖了' % self.name) mutexA.release() print('%s釋放了B鎖' % self.name) mutexB.release() print('%s釋放了A鎖' % self.name) if __name__ == '__main__': for i in range(100): t = Mythread() t.start()
五.Event事件
一些線程須要等待另一些線程運行完畢才能運行,相似於發射信號同樣spa
from threading import Thread from threading import Event import time event = Event() #造了一個綠燈 def light(): print('等紅燈') time.sleep(3) event.set() # 解除阻塞而且給 event發了一個信號 print('綠燈亮了') def car(i): print('%s燈紅燈'%i) event.wait() print('%s綠燈了,加油門'%i) if __name__ == '__main__': t = Thread(target=light) t.start() p_list=[] for i in range(5): p = Thread(target=car,args=(i,)) p.start() # 等紅燈 # 0燈紅燈 # 1燈紅燈 # 2燈紅燈 # 3燈紅燈 # 4燈紅燈 # 綠燈亮了 # 0綠燈了,加油門 # 1綠燈了,加油門 # 3綠燈了,加油門 # 4綠燈了,加油門 # 2綠燈了,加油門#
六.信號量(瞭解)
自定義的互斥鎖若是是一個廁所,那麼信號量就至關於公共廁所,門口掛着多個廁所的鑰匙。搶和釋放跟互斥鎖一致線程
普通互斥鎖, 獨立衛生間 全部人只有一把鎖 信號量 ,公共衛生間 有多少個坑 就有多少把鎖 """ from threading import Thread from threading import Semaphore #信號量 import time import random sm = Semaphore(5) #一個公共廁所造了5個坑 在廁所外放了5把鎖 def task(name): sm.acquire() # 釋放信號鎖 print('%s正在蹲坑'%name) time.sleep(random.randint(1, 3)) sm.release() for i in range(20): t = Thread(target= task,args=('%s傘兵'%i,)) t.start()
0傘兵正在蹲坑 1傘兵正在蹲坑 2傘兵正在蹲坑 3傘兵正在蹲坑 4傘兵正在蹲坑 此時5我的中 有一我的好了 同時釋放了一把鎖 5傘兵正在蹲坑 前面5個好了兩個釋放給 6,7 6傘兵正在蹲坑 7傘兵正在蹲坑 8傘兵正在蹲坑 9傘兵正在蹲坑 10傘兵正在蹲坑 11傘兵正在蹲坑 12傘兵正在蹲坑 13傘兵正在蹲坑 14傘兵正在蹲坑 15傘兵正在蹲坑 16傘兵正在蹲坑 17傘兵正在蹲坑 18傘兵正在蹲坑 19傘兵正在蹲坑
七.線程queue
同一個進程下的線程數據都是共享的爲何還要用queue?queue自己自帶鎖的功能,可以保證數據的安全code
import queue """ 1.普通q 2.堆棧。先進後出q 3.優先級q """ q = queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) # 取值
——————》》》
1
2
3 q = queue.LifoQueue(5) q.put(1) q.put(2) q.put(3) q.put(4) q.put(5) print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 先進後出 和堆棧同樣
——————》》》
5
4
3
2
1
q = queue.PriorityQueue(3) q.put(100,"q") q.put(20,"t") q.put(-1,'s') print(q.get()) print(q.get()) print(q.get()) # 優先級是按照數字從小到大排列的
————————》》》
-120100