前言
在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式,
若只是單純的平行需求,我們可以使用 threading 這個模組來快速完成平行處理的方式。
使用注意:threading 是透過 context-switch 的方式實現
也就是說,我們是透過 CPU 的不斷切換 (context-switch),實現平行的功能。
當大量使用 threading 執行平行的功能時,反而會因為大量的 context-switch,
「實現了程式平行的功能,但也因為大量的 context-switch ,使得程式執行速度更慢」。
thread 與 multiprocess 比較
threading 重點摘要
threading 是透過 context-switch 的方式實現
也就是說,我們是透過 CPU 的不斷切換 (context-switch),實現平行的功能。
當大量使用 threading 執行平行的功能時,反而會因為大量的 context-switch,
「實現了程式平行的功能,但也因為大量的 context-switch ,使得程式執行速度更慢」。
multiprocessing 重點摘要
multiprocessing 在資料傳遞上,會因為需要將資料轉移至其他 CPU 上進行運算,
因此會需要考慮資料搬運的時間,
而多核心真正的實現「平行運算的功能」,當任務較為複雜時,效率一定比較好。
thread 與 multiprocess 比較圖
從下圖我們可以看到任務被完成的「概念」時間
- main 1~4, main-end
- 任務 A1, A2
- 任務 B1, B2
- 任務 C1, C2
請留意圖中粗線的部分:
* 在 multithread 中,
CPU context-switch 會額外消耗我們程式執行的時間,程式實際完成時間可能比一般的還要慢。
- 在 multiprocess 中,
我們需要將資料轉移至其他 CPU 會額外消耗我們程式執行的時間,如果任務過於簡單,效益可能不大。
雖然示意圖中明顯感覺較快,但前提是任務夠複雜
也就是說,「任務難度執行的時間 > 資料轉移至其他 CPU 的時間效益」,不然只會更慢。
Thread 基本使用
範例程式碼 (single-thread)
import threading
def task(a, b):
print('Task in the thread.')
print(a, b)
if __name__=='__main__': # must put thread in the main
t1 = threading.Thread(target=task,args=(1,2))
t1.start()
t1.join()
運行結果
說明
- t1 = threading.Thread(target=task,args=(1,2))
- 建立一個名字為 t1 的 Thread,執行 task 任務,傳入參數 (1,2)
- t1.start():啟動 t1 任務
- t1.join():等待 t1 任務結束 (一定會等到結束才執行下一行)
多個 Thread 同時平行處理,「保證」任務「結果」的順序性 (multi-thread)
範例程式碼,「保證」任務「結果」的順序性 (multi-thread)
import threading
def task(num):
print('This is thread: ', num)
if __name__=='__main__':
num_threads = 5
threads_list = []
for i in range(num_threads):
threads_list.append(threading.Thread(target = task, args = (i,)))
threads_list[i].start()
for i in range(num_threads):
threads_list[i].join()
運行結果 (注意:每次執行不一定相同)
注意:該輸入的都有輸出,但有些順序搶先輸出了,例如:「1 沒有跟在冒號後面」。
所以「每次執行不一定相同」。
說明
- threads_list.append(threading.Thread(target = task, args = (i,)))
- 建立 Thread,存入,執行 task 任務,傳入參數 (i, )
- threads_list[i].start():啟動 threads_list[i] 任務
- threads_list[i].join():等待 threads_list[i] 任務結束
(一定會等到結束才執行下一行,並且確保一定照 i 的順序結束)
此外,使用 Thread 本身就應該要確保,「任務內容的過程,沒有順序性問題」,
多個 Thread 中共用一個變數計算,我們沒辦法保計算出來的結果一定合乎預期。
.
例如:有可能我們單一 thread 預期要對同一變數做「先加後乘」,我們執行兩個 thread,
我們預期的順序是「加乘加乘」,但其實可能輸出是「加加乘乘」的結果。
多個 Thread 同時平行處理,「保證」任務「過程中」的順序 (multi-thread)
這邊的示範,是為了當任務內容順序有高度要求時,
我們必須進行以下的處理,但勢必「因為 Thread 彼此等待,造成全部任務完成的時間會更慢」。
如果有這樣的需求,甚至可以不用 Thread,
因為有 context-switch 並不會比較快,甚至可能還會更慢。
範例程式碼,「保證」任務「過程中」的順序 (multi-thread)
import threading
def task(num):
print('This is thread: ', num)
if __name__=='__main__':
num_threads = 5
threads_list = []
for i in range(num_threads):
threads_list.append(threading.Thread(target = task, args = (i,)))
threads_list[i].start()
threads_list[i].join()
運行結果
如此就能保證我們的順序性了。
但這樣的寫法,不如就直接寫個 for-loop 跑 function 吧
此方法用 thread 真的沒有比較快。
只是能在做 thread 的過程中,能在主程式做一些其他事情。