python 程式、執行緒 (二)
一、多執行緒與多程式的對比
在中簡單的說過,CPython中的GIL使得同一時刻只能有一個執行緒執行,即併發執行。並且即使是多核CPU,GIL使得同一個程式中的多個執行緒也無法對映到多個CPU上執行,這麼做最初是為了安全著想,慢慢的也成為了限制CPython效能的問題。
就像是一個執行緒想要執行,就必須得到GIL,否則就不能拿到CPU資源。但是也不是說一個執行緒在拿到CPU資源後就一勞永逸,在執行的過程中GIL可能會釋放並被其他執行緒獲取,所以說其它的執行緒會與本執行緒競爭CPU資源。
在中有關於GIL釋放和GIL的概要。
多執行緒在python2中:當一個執行緒進行I/O的時候會釋放鎖,另外當ticks計數達到100(ticks可以看作是Python自身的一個計數器,也可對比著位元組碼指令理解,專門做用於GIL,每次釋放後歸零,這個計數可以透過 sys.setcheckinterval 來調整)。鎖釋放之後,就涉及到執行緒的排程,執行緒的鎖進行,執行緒的切換。這是會消耗CPU資源,因此會造成程式效能問題和等待時延。特別是在CPU密集型程式碼時。
但是對於多程式,GIL就無法限制,多個程式可以再多個CPU上執行,充分利用多核優勢。事情往往是相對的,雖然可以充分利用多核優勢,但是程式之間的切換卻比執行緒的切換代價更高。
所以選擇多執行緒還是多程式,主要還是看怎樣權衡代價,什麼樣的情況。
1、CPU密集程式碼
下面來利用斐波那契數列模擬CPU密集運算。
def fib(n): # 求斐波那契數列的第n個值 if n<=2: return 1 return fib(n-1)+fib(n-2)
<1>、多程式
列印第25到35個斐波那契數,並計算程式執行時間
import timefrom concurrent.futures import ThreadPoolExecutor, as_completedfrom concurrent.futures import ProcessPoolExecutordef fib(n): if n<=2: return 1 return fib(n-1)+fib(n-2)if __name__ == "__main__": with ProcessPoolExecutor(3) as executor: # 使用程式池控制 每次執行3個程式 all_task = [executor.submit(fib, (num)) for num in range(25,35)] start_time = time.time() for future in as_completed(all_task): data = future.result() print("exe result: {}".format(data)) print("last time is: {}".format(time.time()-start_time))# 輸出exe result: 75025exe result: 121393exe result: 196418exe result: 317811exe result: 514229exe result: 832040exe result: 1346269exe result: 2178309exe result: 3524578exe result: 5702887last time is: 4.457437038421631
輸出結果,每次列印三個exe result,總重列印十個結果,多程式執行時間為4.45秒
<2>、多執行緒
import timefrom concurrent.futures import ThreadPoolExecutor, as_completedfrom concurrent.futures import ProcessPoolExecutordef fib(n): if n<=2: return 1 return fib(n-1)+fib(n-2)if __name__ == "__main__": with ThreadPoolExecutor(3) as executor: # 使用執行緒池控制 每次執行3個執行緒 all_task = [executor.submit(fib, (num)) for num in range(25,35)] start_time = time.time() for future in as_completed(all_task): data = future.result() print("exe result: {}".format(data)) print("last time is: {}".format(time.time()-start_time))# 輸出exe result: 121393exe result: 75025exe result: 196418exe result: 317811exe result: 514229exe result: 832040exe result: 1346269exe result: 2178309exe result: 3524578exe result: 5702887last time is: 7.3467772006988525
最終程式執行時間為7.34秒
程式的執行之間與計算機的效能有關,每天計算機的執行時間都會有差異。從上述結果中看顯然多執行緒比多程式要耗費時間。這就是因為對於密集程式碼(密集運算,迴圈語句等),tick計數很快達到100,GIL來回的釋放競爭,執行緒之間頻繁切換,所以對於密集程式碼的執行中,多執行緒效能不如對程式。
2、I/O密集程式碼
一個執行緒在I/O阻塞的時候,會釋放GIL,掛起,然後其他的執行緒會競爭CPU資源,涉及到執行緒的切換,但是這種代價與較高時延的I/O來說是不足為道的。
下面用sleep函式模擬密集I/O
def random_sleep(n): time.sleep(n) return n
<1>、 多程式
def random_sleep(n): time.sleep(n) return nif __name__ == "__main__": with ProcessPoolExecutor(5) as executor: all_task = [executor.submit(random_sleep, (num)) for num in [2]*30] start_time = time.time() for future in as_completed(all_task): data = future.result() print("exe result: {}".format(data)) print("last time is: {}".format(time.time()-start_time)) # 輸出 exe result: 2exe result: 2......(30個) exe result: 2exe result: 2last time is: 12.412866353988647
每次列印5個結果,總共二十個列印結果,多程式執行時間為12.41秒
<2>、多執行緒
def random_sleep(n): time.sleep(n) return nif __name__ == "__main__": with ThreadPoolExecutor(5) as executor: all_task = [executor.submit(random_sleep, (num)) for num in [2]*30] start_time = time.time() for future in as_completed(all_task): data = future.result() print("exe result: {}".format(data)) print("last time is: {}".format(time.time()-start_time)) # 輸出 exe result: 2exe result: 2......(30個) exe result: 2exe result: 2last time is: 12.004231214523315
I/O密集多執行緒情況下,程式的效能較多程式有了略微的提高。IO密集型程式碼(檔案處理、網路爬蟲等),多執行緒能夠有效提升效率(單執行緒下有IO操作會進行IO等待,造成不必要的時間浪費,而開啟多執行緒能線上程A等待時,自動切換到執行緒B,可以不浪費CPU的資源,從而能提升程式執行效率)。所以python的多執行緒對IO密集型程式碼比較友好。
3、總結
CPU密集型程式碼(各種迴圈處理、計數等等),多執行緒效能不如多程式。
I/O密集型程式碼(檔案處理、網路爬蟲等),多程式不如多執行緒。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3705/viewspace-2817806/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- python 程式、執行緒 (一)Python執行緒
- Python程式VS執行緒Python執行緒
- Python筆記二之多執行緒Python筆記執行緒
- Python 中執行緒和程式Python執行緒
- Python 多執行緒多程式Python執行緒
- Python 多執行緒及程式Python執行緒
- Python 多執行緒無用?深入總結 二(深入瞭解GIL 執行緒守護 執行緒程式CPU關係)Python執行緒
- Python——程式、執行緒、協程、多程式、多執行緒(個人向)Python執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- 二. 執行緒管理之執行緒池執行緒
- 多執行緒程式設計基礎(二)-- 執行緒池的使用執行緒程式設計
- Python執行緒池與程式池Python執行緒
- Python程式與執行緒知識Python執行緒
- python 多執行緒程式設計Python執行緒程式設計
- Python多執行緒程式設計Python執行緒程式設計
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 程式執行緒篇——程式執行緒基礎執行緒
- Java執行緒池二:執行緒池原理Java執行緒
- 什麼是Python執行緒?Python執行緒如何建立?Python執行緒
- 多執行緒(二)執行緒
- 執行緒安全(二)執行緒
- Python並行程式設計(二):多執行緒鎖機制利用Lock與RLock實現執行緒同步Python並行行程程式設計執行緒
- 入門python多執行緒/多程式Python執行緒
- Python的多程式和多執行緒Python執行緒
- Python學習之程式和執行緒Python執行緒
- Java併發程式設計之執行緒篇之執行緒簡介(二)Java程式設計執行緒
- 程式-程式-執行緒執行緒
- 面試集錦(二)程式與執行緒面試執行緒
- python之多執行緒Python執行緒
- PyQt應用程式中的多執行緒:使用Qt還是Python執行緒?QT執行緒Python
- [短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全執行緒程式設計
- python多執行緒中:如何關閉執行緒?Python執行緒
- python基礎執行緒-管理併發執行緒Python執行緒
- 多執行緒與高併發(二)執行緒安全執行緒
- 程式執行緒篇——執行緒切換(上)執行緒
- 程式執行緒篇——執行緒切換(下)執行緒
- 執行緒、執行緒與程式、ULT與KLT執行緒