事實上,Python 多執行緒另一個很重要的話題叫,GIL(Global Interpreter Lock,即全域解釋器鎖),
多執行緒不一定比單執行緒快
在Python中,可以通過多行程、多執行緒和多協程來實作多任務,難道多執行緒就一定比單執行緒快?
下面我用一段代碼證明我自己得觀點,
'''
@Author: Runsen
@微信公眾號: Python之王
@博客: https://blog.csdn.net/weixin_44510615
@Date: 2020/6/4
'''
import threading, time
def my_counter():
i = 0
for _ in range(100000000):
i = i+1
return True
def main1():
start_time = time.time()
for tid in range(2):
t = threading.Thread(target=my_counter)
t.start()
t.join() # 第一次回圈的時候join方法引起主執行緒阻塞,但第二個執行緒并沒有啟動,所以兩個執行緒是順序執行的
print("單執行緒順序執行total_time: {}".format(time.time() - start_time))
def main2():
thread_ary = {}
start_time = time.time()
for tid in range(2):
t = threading.Thread(target=my_counter)
t.start()
thread_ary[tid] = t
for i in range(2):
thread_ary[i].join() # 兩個執行緒均已啟動,所以兩個執行緒是并發的
print("多執行緒執行total_time: {}".format(time.time() - start_time))
if __name__ == "__main__":
main1()
main2()
復制代碼
運行結果
單執行緒順序執行total_time: 17.754502773284912
多執行緒執行total_time: 20.01178550720215
復制代碼
我怕你說我亂得出來得結果,我還是截個圖看清楚點
這時,我懷疑:我的機器出問題了嗎?其實不是這樣,本質上來說Python 的執行緒失效了,沒有起到并行計算的作用,
Python 的執行緒,的確封裝了底層的作業系統執行緒,在 Linux 系統里是 Pthread(全稱為 POSIX Thread),而在 Windows 系統里是 Windows Thread,另外,Python 的執行緒,也完全受作業系統管理,比如協調何時執行、管理記憶體資源、管理中斷等等,
GIL不是Python的特性
GIL 的概念用簡單的一句話來解釋,就是任一時刻,無論執行緒多少,單一 CPython 解釋器只能執行一條位元組碼,這個定義需要注意的點:
首先需要明確的一點是GIL并不是Python的特性,它是在實作Python決議器(CPython)時所引入的一個概念,
C++是一套語言(語法)標準,但是可以用不同的編譯器來編譯成可執行代碼,有名的編譯器例如GCC,INTEL C++,Visual C++等,
Python也一樣,同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行,
其他 Python 解釋器不一定有 GIL,例如 Jython (JVM) 和 IronPython (CLR) 沒有 GIL,而 CPython,PyPy 有 GIL;
因為CPython是大部分環境下默認的Python執行環境,所以在很多人的概念里CPython就是Python,也就想當然的把GIL歸結為Python語言的缺陷,所以這里要先明確一點:GIL并不是Python的特性,Python完全可以不依賴于GIL
GIL本質就是一把互斥鎖
GIL本質就是一把互斥鎖,既然是互斥鎖,所有互斥鎖的本質都一樣,都是將并發運行變成串行,以此來控制同一時間內共享資料只能被一個任務所修改,進而保證資料安全,
可以肯定的一點是:保護不同的資料的安全,就應該加不同的鎖,
GIL 的作業原理:比如下面這張圖,就是一個 GIL 在 Python 程式的作業示例,其中,Thread 1、2、3 輪流執行,每一個執行緒在開始執行時,都會鎖住 GIL,以阻止別的執行緒執行;同樣的,每一個執行緒執行完一段后,會釋放 GIL,以允許別的執行緒開始利用資源,
計算密集型
計算密集型任務的特點是要進行大量的計算,消耗CPU資源,
我們先來看一個簡單的計算密集型示例:
'''
@Author: Runsen
@微信公眾號: Python之王
@博客: https://blog.csdn.net/weixin_44510615
@Date: 2020/6/4
'''
import time
COUNT = 50_000_000
def count_down():
global COUNT
while COUNT > 0:
COUNT -= 1
s = time.perf_counter()
count_down()
c = time.perf_counter() - s
print('time taken in seconds - >:', c)
time taken in seconds - >: 9.2957003
復制代碼
這個是單執行緒, 時間是9s, 下面我們用兩個執行緒看看結果又如何:
'''
@Author: Runsen
@微信公眾號: Python之王
@博客: https://blog.csdn.net/weixin_44510615
@Date: 2020/6/4
'''
import time
from threading import Thread
COUNT = 50_000_000
def count_down():
global COUNT
while COUNT > 0:
COUNT -= 1
s = time.perf_counter()
t1 = Thread(target=count_down)
t2 = Thread(target=count_down)
t1.start()
t2.start()
t1.join()
t2.join()
c = time.perf_counter() - s
print('time taken in seconds - >:', c)
time taken in seconds - >: 17.110625
復制代碼
我們程式主要的操作就是在計算, CPU沒有等待, 而改為多執行緒后, 增加了執行緒后, 在執行緒之間頻繁的切換,增大了時間開銷, 時間當然會增加了,
還有一種型別是IO密集型,涉及到網路、磁盤IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低于CPU和記憶體的速度),對于IO密集型任務,任務越多,CPU效率越高,但也有一個限度,常見的大部分任務都是IO密集型任務,比如Web應用,
總結:對于io密集型作業(Python爬蟲),多執行緒可以大幅提高代碼效率,對CPU計算密集型(Python資料分析,機器學習,深度學習),多執行緒的效率可能比單執行緒還略低,所以,資料領域沒有多執行緒提高效率之說,只有將CPU提升到GPU,TPU來提升計算能力,
作者:劉潤森
鏈接:https://juejin.im/post/6892747972933910535
來源:掘金
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/207972.html
標籤:其他
