1、全局解釋器鎖 (GIL)
運行test.py的流程:
a、將python解釋器的代碼從硬盤讀入內存
b、將test.py的代碼從硬盤讀入內存 (一個進程內裝有兩份代碼---一份cpython解釋器代碼一份test.py代碼)
c、將test.py中的代碼像 字符串同樣 讀入python解釋器中解析執行
1 、GIL:全局解釋器鎖 (
CPython解釋器的特性)
#本質就是一把互斥鎖,至關於執行權限,每一個進程內都會存在一把GIL
#同一進程的多個線程必須搶到GIL才能使用CPython解釋器解釋執行本身的代碼,
#同一進程的多線程沒法實現並行,但能夠實現併發
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(
若是沒有gil 多線程拿到解釋器 並行(如今計算機都是多cup),當x=10的線程中內存中產生一個10,還沒來的及綁定x,就有可能被垃圾回收機制回收,爲了保證數據安全加gil鎖).However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
GIL本質就是一把加在解釋器身上的互斥鎖(執行權限)。同一個進程內的全部線程都須要先搶到GIL鎖,才能執行解釋器代碼
二、GIL的優缺點:
優勢:保證Cpython解釋器內存管理的線程安全
缺點:(一個進程內的線程只能一個一個併發執行)在Cpython解釋器中,同一個進程下開啓的多線程,同一時刻只能有一個線程執行,也就說Cpython解釋器的多線程沒法實現並行沒法利用多核優點
注意:
a、GIL不能並行,但有可能併發,不必定爲串行。(由於串行是一個任務完徹底全執行完畢後才進行下一個;而
cpython中,一個線程在io時,被CPU釋放時,會被強行取消GIL的使用權限)
b、多核(多CPU)的優點是提高運算效率
c、計算密集型--》使用多進程,用上多核,同時存在(cup數的進程)數並行
多進程:建立新進程(拷貝),切換進程---》開銷大(時間上,內存上)
d、IO密集型--》使用多線程 併發
多線程:建立新線程,切換線程----》開銷小(時間上,內存上)
2、Cpython解釋器併發效率驗證
一、計算密集型應該使用多進程
from multiprocessing import Process
from threading import Thread
import time
# import os
# print(
os.cpu_count()) #查看cpu個數
def task1():
res=0
for i in range(1,100000000):
res+=i
def task2():
res=0
for i in range(1,100000000):
res+=i
def task3():
res=0
for i in range(1,100000000):
res+=i
def task4():
res=0
for i in range(1,100000000):
res+=i
if __name__ == '__main__':
# p1=Process(target=task1)
# p2=Process(target=task2)
# p3=Process(target=task3)
# p4=Process(target=task4)
p1=Thread(target=task1)
p2=Thread(target=task2)
p3=Thread(target=task3)
p4=Thread(target=task4)
start_time=time.time()
p1.start()
p2.start()
p3.start()
p4.start()
p1.join()
p2.join()
p3.join()
p4.join()
stop_time=time.time()
print(stop_time - start_time)
二、IO密集型應該使用多線程
from multiprocessing import Process
from threading import Thread
import time
def task1():
time.sleep(3)
def task2():
time.sleep(3)
def task3():
time.sleep(3)
def task4():
time.sleep(3)
if __name__ == '__main__':
# p1=Process(target=task1)
# p2=Process(target=task2)
# p3=Process(target=task3)
# p4=Process(target=task4)
# p1=Thread(target=task1)
# p2=Thread(target=task2)
# p3=Thread(target=task3)
# p4=Thread(target=task4)
# start_time=time.time()
# p1.start()
# p2.start()
# p3.start()
# p4.start()
# p1.join()
# p2.join()
# p3.join()
# p4.join()
# stop_time=time.time()
# print(stop_time - start_time) #3.138049364089966
p_l=[]
start_time=time.time()
for i in range(500):
p=Thread(target=task1)
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(time.time() - start_time)
3、線程互斥鎖與GIL對比
cpython中
GIL能保護解釋器級別代碼(和垃圾回收機制有關)但保護不了其餘共享數據(好比本身的代碼)。因此在程序中對於須要保護的數據要自行加鎖
gil只是保證一個進程內的全部線程都是併發(不是串行嗎)執行,[一個線程在io時,被CPU釋放時,會被強行取消GIL的使用權限]
主線程,線程1,線程2,垃圾回收線程》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
線程1搶到gil 當拿到cpu 解釋執行代碼:搶到互斥鎖 睡 (切—剝奪cpu-強行釋放gil鎖)改數據 放開互斥鎖
線程2搶到gil 當拿到cpu 解釋執行代碼:發現互斥鎖還沒釋放等(切-剝奪cpu,強行釋放gil)
線程3。。。
線程2。。。。
垃圾線程搶到gil,當拿到cpu 解釋執行代碼:回收引用計數爲0的數據.....
線程1搶到gil,當拿到cpu 解釋執行代碼:改數據 放開互斥鎖
。。。。。。。。。。。。。。
from threading import Thread,Lock
import time
mutex=Lock()
count=0
def task():
global count
mutex.acquire()
temp=count
time.sleep(0.1)
count=temp+1
mutex.release()
if __name__ == '__main__':
t_l=[]
for i in range(2):
t=Thread(target=task)
t_l.append(t)
t.start()
for t in t_l:
t.join()
print('主',count)