-
什麼是GIL
全域性直譯器鎖(英語:Global Interpreter Lock,縮寫GIL),是計算機程式設計語言直譯器用於同步執行緒的一種機制,它使得任何時刻僅有一個執行緒在執行。即便在多核心處理器上,使用 GIL 的直譯器也只允許同一時間執行一個執行緒。常見的使用 GIL 的直譯器有CPython與Ruby MRI。 -- 維基百科
一般用的Python的直譯器就是CPython(官網下載的版本),結合上面對GIL的解釋,得出兩個結論:
-
Python中的多執行緒並不是同時有多個執行緒並行執行,而是不斷的切換執行緒達到多執行緒並行的效果,同一時間只能執行一個執行緒。
-
Python的多執行緒程式並不能利用多核CPU的優勢,無論有多少個執行緒,只能執行在一個核上。
-
-
測試GIL
import threading import time from queue import Queue def normal(num_list): total = sum(num_list) print(total) def mutilThread_sum(l, q): q.put(sum(l)) def mutilThread(num_list): q = Queue() threads = [] total = 0 for idx in range(4): thread = threading.Thread(target=mutilThread_sum, args=(num_list[:], q)) thread.start() threads.append(thread) [thread.join() for thread in threads] for thread in threads: total += q.get() print(total) num_list = list(range(1000000)) normal_start = time.time() normal(num_list*4) print('normal:', time.time() - normal_start) mutilThread_start = time.time() mutilThread(num_list) print('mutilThread:', time.time() - mutilThread_start) 複製程式碼
結果:
單執行緒計算的是多執行緒中每個執行緒計算的4倍,理論上多執行緒應該比單執行緒快3~4倍左右,但是從結果看來mutilThread並沒有比normal快那麼多,而且如果多執行幾次你還會發現多執行緒有時候還會比單執行緒慢。這就是因為GIL的存在導致的。
-
多程式
雖然無法使用多執行緒利用多核CPU的優勢,但是可以使用多程式達到這一目的。
-
multiprocessing
Python中多程式需要藉助multiprocessing模組實現。這個模組和Threading模組很像。
from multiprocessing import Process def add_one(a): print(a + 1) p1 = Process(target=add_one, args=(1,)) # 建立一個程式 p1.start() # 啟動程式 p1.join() # 等待程式結束 複製程式碼
-
程式中函式的輸出
觀察上面例子中的函式,如果把
print(a + 1)
換為return a + 1
。那麼怎樣才能拿到這個函式的返回值?用佇列。from multiprocessing import Process, Queue def add_one(q, a): q.put(a + 1) q = Queue() p1 = Process(target=add_one, args=(q, 1)) p2 = Process(target=add_one, args=(q, 10)) p1.start() p2.start() p1.join() p2.join() a = q.get() b = q.get() print(a + b) # 13 複製程式碼
-
-
簡單的多程式,多執行緒效率對比
from multiprocessing import Process, Queue from threading import Thread import time def calculate(q): res = 0 for i in range(10000000): res += i ** 2 q.put(res) # 多程式 def multi_process(): q = Queue() p1 = Process(target=calculate, args=(q,)) p2 = Process(target=calculate, args=(q,)) p1.start() p2.start() p1.join() p2.join() res = q.get() + q.get() print('multi_process:', res) # 多執行緒 def multi_thread(): q = Queue() p1 = Thread(target=calculate, args=(q,)) p2 = Thread(target=calculate, args=(q,)) p1.start() p2.start() p1.join() p2.join() res = q.get() + q.get() print('multi_thread:', res) # 單執行緒 def normal(): res = 0 for i in range(2): for j in range(10000000): res += j ** 2 print('normal:', res) t1 = time.time() normal() t2 = time.time() print('normal time:', t2 - t1) multi_process() t3 = time.time() print('multi_process time:', t3 - t2) multi_thread() t4 = time.time() print('multi_thread time:', t4 - t3) 複製程式碼
結果:
對比一下,多程式效率是最高的,當然了這個例子是屬於計算密集型的例子,在io密集型的程式上,多執行緒的優勢還是挺大的。
-
程式池
如果需要大量的建立程式,那麼就可以使用程式池這個功能:Pool
from multiprocessing import Pool from os import getpid import time import random def square(num): print('程式id:%s啟動' % getpid()) time.sleep(random.random() * 3) print('程式id:%s執行完畢' % getpid()) return num ** 2 p = Pool() results = [] for i in range(10): res = p.apply_async(square, args=(i,)) results.append(res) p.close() p.join() for result in results: print(result.get()) 複製程式碼
結果:
解釋:
-
pool:用於建立一個程式池物件,可以傳入同時跑的程式的數量,預設的程式數是cpu的核數。比如:
p = Pool(processes=10) 複製程式碼
這樣就可以讓10個程式同時跑,仔細觀察上面例子的結果,前8個程式是立即就啟動了,因為沒有給pool方法傳入程式數量,預設就是cpu核的數量。我這裡是8,所以同時只能跑8個程式,當這八個程式中的某一個程式的任務執行完成後才會啟動新的程式。
-
apply_async:啟動程式,執行引數中傳入的函式,返回的結果使用get方法獲取,注意get是會阻塞的,也就是說如果寫成下面這樣的話,就變成了一個程式執行完成再啟動下一個程式,和單程式沒什麼兩樣了:
for i in range(10): res = p.apply_async(square, args=(i,)) res.get() 複製程式碼
-
close:就是關閉pool,不再接受新的程式。
-
join:等待所有的子程式執行完畢,在呼叫join之前要先呼叫close或者terminate方法,terminate方法是結束工作的程式,不管任務是否執行完成。
如果不喜歡apply_async這種,還可以使用map,例子:
from multiprocessing import Pool from os import getpid import time import random def square(num): print('程式id:%s啟動' % getpid()) time.sleep(random.random() * 3) print('程式id:%s執行完畢' % getpid()) return num ** 2 p = Pool() res = p.map(square, range(10)) p.close() p.join() print(res) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 複製程式碼
-
Python學習筆記 - 多程式
相關文章
- Python學習筆記|Python之程式Python筆記
- Python學習筆記—程式碼Python筆記
- Python學習筆記 - 多執行緒Python筆記執行緒
- Python學習筆記Python筆記
- 【學習筆記】python筆記Python
- (十五) 學習筆記: Python程式(Process)相關筆記Python
- python學習筆記(1Python筆記
- Python學習筆記 - queuePython筆記
- Effective Python學習筆記Python筆記
- python——Matplotlib學習筆記Python筆記
- python學習筆記4Python筆記
- Python學習筆記 - asyncioPython筆記
- Python 學習筆記(一)Python筆記
- Python學習筆記 - aiohttpPython筆記AIHTTP
- python學習筆記(二)Python筆記
- Python學習筆記(2)Python筆記
- python——numpy學習筆記Python筆記
- Python學習筆記(三)Python筆記
- 多項式學習筆記筆記
- python程式設計學習筆記⑦-1函式Python程式設計筆記函式
- php多程式插入資料(pcntl 學習筆記二)PHP筆記
- 【Python3學習筆記】之【Python高階——多執行緒】Python筆記執行緒
- Linux 學習筆記--程式Linux筆記
- Python學習筆記 - 閉包Python筆記
- Python學習筆記 - 作用域Python筆記
- Python學習筆記 - 變數Python筆記變數
- Python學習筆記 - time, datetimePython筆記
- Python學習筆記 - if語句Python筆記
- Python學習筆記——turtle庫Python筆記
- python-pygame學習筆記PythonGAM筆記
- Python基礎學習筆記Python筆記
- 字典--Python學習筆記(五)Python筆記
- Python學習筆記|Python之yield理解Python筆記
- Python學習筆記|Python之索引迭代Python筆記索引
- Python學習筆記|Python之特殊方法Python筆記
- [寒假學習筆記](二)Python初學筆記Python
- 【Python學習】學習筆記 14-15 字串Python筆記字串
- Linux 學習筆記--程式管理Linux筆記