python究竟要不要使用多线程
閱讀目錄
- 1. 先來看兩個例子
- 2. python虛擬機機制如何控制代碼執行?
- 3. python多線程究竟有沒有用?
- 4. python多進程執行原理
在總結concurrent.futures庫之前先來弄明白三個問題:回到頂部(1)python多線程究竟有沒有用?
(2)python虛擬機機制如何控制代碼的執行?
(3)python中多進程處理原理是怎么樣的?
1. 先來看兩個例子
(1)例1
分別用單線程、使用多線程、使用多進程三種方法對最大公約數進行計算
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
def gcd(pair):??? a, b = pair
??? low = min(a, b)
??? for i in range(low, 0, -1):
??????? if a % i == 0 and b % i == 0:
??????????? return i numbers = [
??? (1963309, 2265973), (1879675, 2493670), (2030677, 3814172),
??? (1551645, 2229620), (1988912, 4736670), (2198964, 7876293)
if __name__ == '__main__':# 不使用多線程和多進程start = time.time()results = list(map(gcd,numbers))end = time.time()print('未使用--timestamp:{:.3f} second'.format(end-start))#使用多線程start = time.time()pool = ThreadPoolExecutor(max_workers=3)results = list(pool.map(gcd,numbers))end = time.time()print('使用多線程--timestamp:{:.3f} second'.format(end-start))#使用多進程start = time.time()pool = ProcessPoolExecutor(max_workers=3)results = list(pool.map(gcd,numbers))end = time.time()print('使用多進程程--timestamp:{:.3f} second'.format(end-start)) 輸出:
之前線程數和進程說都為3,現在修改為4再測試
為了更能說明問題,將線程數和進程說繼續增加為5
至于區別,大家自己感受,測試的條件(計算過于簡單)、測試的環境都會影響測試結果
(2)例2
同樣分別用單線程、使用多線程、使用多進程三種方法對網頁進行爬蟲,只是簡單的返回status_code
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
import requestsdef download(url):headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0','Connection':'keep-alive','Host':'example.webscraping.com'}response = requests.get(url, headers=headers)return(response.status_code)if __name__ == '__main__':urllist = ['http://example.webscraping.com/places/default/view/Afghanistan-1','http://example.webscraping.com/places/default/view/Aland-Islands-2','http://example.webscraping.com/places/default/view/Albania-3','http://example.webscraping.com/places/default/view/Algeria-4','http://example.webscraping.com/places/default/view/American-Samoa-5']start = time.time() result = list(map(download, urllist))end = time.time()print('status_code:',result)print('未使用--timestamp:{:.3f}'.format(end-start))pool = ThreadPoolExecutor(max_workers = 3)start = time.time() result = list(pool.map(download, urllist))end = time.time()print('status_code:',result)print('使用多線程--timestamp:{:.3f}'.format(end-start))pool = ProcessPoolExecutor(max_workers = 3)start = time.time() result = list(pool.map(download, urllist))end = time.time()print('status_code:',result)print('使用多進程程--timestamp:{:.3f}'.format(end-start)) ? 輸出:
一下就看出了區別
回到頂部2. python虛擬機機制如何控制代碼執行?
對于python來說,作為解釋型語言,Python的解釋器必須做到既安全又高效。我們都知道多線程編程會遇到的問題,解釋器要留意的是避免在不同的線程操作內部共享的數據,同時它還要保證在管理用戶線程時保證總是有最大化的計算資源。python是通過使用全局解釋器鎖來保護數據的安全性。
python 代碼的執行由python虛擬機來控制,即Python先把代碼(.py文件)編譯成字節碼(字節碼在Python虛擬機程序里對應的是 PyCodeObject對象,.pyc文件是字節碼在磁盤上的表現形式),交給字節碼虛擬機,然后虛擬機一條一條執行字節碼指令,從而完成程序的執行。 python在設計的時候在虛擬機中,同時只能有一個線程執行。同樣地,雖然python解釋器中可以運行多個線程,但在任意時刻,只有一個線程在解釋器 中運行。而對python虛擬機的訪問由全局解釋器鎖來控制,正是這個鎖能保證同一時刻只有一個線程在運行。
在多線程的環境中,python虛擬機按一下 方式執行:
(1)設置GIL(global interpreter lock)
(2)切換到一個線程執行
(3)運行:指定數量的字節碼指令、線程主動讓出控制(可以調用time.sleep(0))
(4)把線程設置為睡眠狀態
(5)解鎖GIL
(6)再次重復以上步驟。
GIL的特性,也就導致了python不能充分利用多核cpu。而 對面向I/O的(會調用內建操作系統C代碼的)程序來說,GIL會在這個I/O調用之前被釋放,以允許其他線程在這個線程等待I/O的時候運行。如果線程 并未使用很多I/O操作,它會在自己的時間片一直占用處理器和GIL。
回到頂部3. python多線程究竟有沒有用?
通過前面的例子和python虛擬機制的理解對多線程的使用應該很清楚了,I/O密集型python程序比計算密集型的程序更能充分利用多線 程的好處。 總之,在計算密集型的程序中不要python多線程,使用python多進程進行并發編程,就不會有GIL這種問題存在,并且也能充分利用多核cpu。
(1)GIL不是bug,Guido也不是水平有限才留下這么個東西。龜叔曾經說過,嘗試不用GIL而用其他的方式來做線程安全,結果python語言整體效率又下降了一倍,權衡利弊,GIL是最好的選擇——不是去不掉,而是故意留著的
(2)想讓python計算速度快起來,又不想寫C,用pypy吧,這才是真正的大殺器
(3)可以使用協程來提高cpu的利用率,使用multiprocessing和gevent
回到頂部4. python多進程執行原理
ProcessPoolExecutor類會利用multiprocessing模塊所提供的底層機制,以例2作為例子描述下多進程執行流程:
(1)把urllist列表中的每一項輸入數據都傳給map
(2)用pickle模塊對數據進行序列化,將其變成二進制形式
(3)通過本地套接字,將序列化之后的數據從解釋器所在的進程發送到子解釋器所在的進程
(4)在子進程中,用pickle對二進制數據進行反序列化,將其還原成python對象
(5)引入包含download函數的python模塊
(6)各個子進程并行的對各自的輸入數據進行計算
(7)對運行的結果進行序列化操作,將其轉變成字節
(8)將這些字節通過socket復制到主進程之中
(9)主進程對這些字節執行反序列化操作,將其還原成python對象
(10)最后把每個子進程所求出的計算結果合并到一份列表之中,并返回給調用者。
multiprocessing開銷比較大,原因就在于:主進程和子進程之間通信,必須進行序列化和反序列化的操作
總結
以上是生活随笔為你收集整理的python究竟要不要使用多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python多线程(3)——Queue模
- 下一篇: Adam那么棒,为什么还对SGD念念不忘