➣ Reading Time: 18 minutes

前言

這一篇文是上一篇文的進階版,若還沒有看過上集可以看這篇:

這篇會更著重在說明「multiprocessing Pool」的用法,
差別在會使用 multiprocessing Pool 可取得回傳結果,並讓系統自動分配資源
而使用 Process 不會回傳結果 (除非使用 mp.Queue 等等方式)

一樣的前言介紹

在 python 中有 thread 與 multiprocess 兩種平行處理程式的方式,
若只是單純的平行需求,我們可以使用 threading 這個模組來快速完成平行處理的方式。
但是 threading 只是透過頻繁的 CPU context-switch 的方式實現,
要真正實現多核心 CPU 的平行運算,我們需要使用 multiprocessing,
將任務指派給多個核心進行操作。

multiprocessing 在資料傳遞上,會因為需要將資料轉移至其他 CPU 上進行運算,
因此會需要考慮資料搬運的時間,
而多核心真正的實現「平行運算的功能」,當任務較為複雜時,效率一定比較好。

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 的時間效益」,不然只會更慢。

multiprocess Pool 的使用 (with map)

這邊再提一次,這一篇文是上一篇文的進階版,若還沒有看過上集可以看這篇:

使用 multiprocessing Pool 可取得回傳結果,並讓系統自動分配資源
而使用 Process 不會回傳結果 (除非使用 mp.Queue 等等方式)

而這邊我們執行 pool 內的任務的時候,使用「map」將任務一個個分配下去

範例程式碼 (multiprocess Pool)

import multiprocessing as mp

def task(num):
  return 'The pool return result: ' + str(num)

if __name__=='__main__':
  pool = mp.Pool()
  res = pool.map(task, range(10))
  print(res)

運行結果

說明

  • pool = mp.Pool()

此外,Pool 內可以使用參數 processes = 「想要的 CPU 核數量」,
例如:pool = mp.Pool(processes=4) ,就是我們指定了 4個 CPU 核。
(預設就是 CPU 的對應核心數量)

  • res = pool.map(task, range(10))

以 map 的方式,將 list 中一個個的參數傳入,
例如此處 range(10) 其實就等於 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
傳入此 10 個任務,並得到對應的 10 個結果回傳。

  • print(res)

印出結果,注意結果都會被「存在一個 list 裡面」,
如果想要單獨的結果,需要一個個拿出來。 (如上圖)

multiprocess Pool 的使用 (with apply_async)

map, apply_async 都是能讓 pool 開始執行任務的方法,
差別在於 map 可以傳入一個 list,
而 apply_async 只能傳入一個 變數。

範例程式碼 (multiprocess Pool)

import multiprocessing as mp


def task(num):
  return 'The pool return result: ' + str(num)

if __name__=='__main__':
  pool = mp.Pool()
  res = pool.apply_async(task, (0,))
  print(res)
  print(res.get()) # use get() to get apply_async result

運行結果

說明

  • pool = mp.Pool()

Pool 內可以使用參數 processes = 「想要的 CPU 核數量」,
例如:pool = mp.Pool(processes=4) ,就是我們指定了 4個 CPU 核。
(預設就是 CPU 的對應核心數量)

  • res = pool.apply_async(task, (0,))

apply_async 的方式不同於 map,一次只能傳入一個變數
例如:此處只能傳進 0,而得到的結果是封裝好的 applyResult,
我們需要再使用 get() 來得到結果。

  • print(res.get())

如同上述說明,res 是封裝好的 applyResult,
我們需要再使用 get() 來得到 apply_async 的分析結果。

multiprocess Pool 的使用 (使用 apply_async 達到 map 的效果)

我們說 apply_async 只能一次執行一個,
難道就不能讓他跑很多次,讓他有類似 map 的效果嗎?

當然是可以的,把任務做成 list (迭代) 的樣子就可以了!

範例程式碼 (使用 apply_async 達到 map 的效果)

import multiprocessing as mp

def task(num):
  return 'The pool return result: ' + str(num)

if __name__=='__main__':
  pool = mp.Pool()
  res_list = []
  for i in range(10):
    res_list.append(pool.apply_async(task, (i,)))

  for res in res_list:
    print(res.get())

運行結果

說明

可以看到結果都是一樣的 (輸出方式稍微改變一下我們先不管)。

你可能會想問? 那 apply_async 與 map 到底差在哪?????

我們直接講重點:

兩種情況各有各自使用的地方,請依照自己的需求選擇使用。

  • map:會等待 map 的任務執行完後,才執行接下來的主程式
  • apply_async:不等待 apply_async 的任務執行完,就會執行接下來的主程式

範例程式碼 – apply_async 與 map 的差別

以 map 執行數到 100 的任務 (會等待才做 main 下一行)

import multiprocessing as mp

def task(num):
  for i in range(num):
    print(i)
  return 'The pool return result: ' + str(num)

if __name__=='__main__':
  pool = mp.Pool()
  res = (pool.map(task, (100,)))
  print("map wait the process finished.") 
  print(res)

執行結果 (只截重點,可以看到 map 等到任務完成才做)

以 apply_async 執行數到 100 的任務 (不會等待才做 main 下一行)

import multiprocessing as mp

def task(num):
  for i in range(num):
    print(i)
  return 'The pool return result: ' + str(num)

if __name__=='__main__':
  pool = mp.Pool()
  res = pool.apply_async(task, (100,))
  print("apply_async do NOT wait the process finished.")  
  print(res.get())

執行結果 (只截重點,可以看到 apply_async 不會等到任務完成才做)

結論

我們知道 apply_async 與 map 的差別後,
實際上,我們就看我們的任務,「main 有沒有需要等待才做下一行」,
就可以決定我們要使用 apply_async 還是 map 囉!

Reference

https://iter01.com/17324.html

⭐Python 基礎用法 相關文章整理⭐:
1.【Python】python list 清除, 移除內容元素 remove, pop, del, clear相關用法整理 sample code (內含範例程式碼)
2.【Python】寫模組 module、package 總整理 Importing files from different folder
3.【Python】python assert (斷言) 用法 sample code (內含範例程式碼)
4.【Python】python 一行 if else 語法 (one line if else) sample code (內含範例程式碼)
5.【Python】lambda 與 def function 使用方法與比較整理(內含範例程式碼)
6.【Python】python map 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code
7.【Python】python zip 使用方法 與 其他寫法比較整理 (內含範例程式碼) sample code
⭐Python 字串處理 相關文章整理⭐:
1.【Python】python print 變數的值 & 變數名稱 方法總整理
2.【Python】python string format str.format 總整理
⭐Python 檔案處理 相關文章整理⭐:
1.【Python】python 開關檔範例 與 程式模板 with open / file open sample code
2.【Python】取出檔案名稱 (含副檔名、不含副檔名) os path basename split 取出 檔名 路徑 不要副檔名 sample code
3.【Python】在 python 中利用 os.chmod 更改檔案的權限 chmod 777
4.【Python】利用 shutil 來複製檔案 shutil copy file
5.【Python】python 建立資料夾範例 mkdir os.makedirs() sample code
6.【Python】python 移除資料夾範例 rmdir shutil.rmtree() sample code
7.【Python】確認檔案是否存在 os.path.isfile / 確認資料夾是否存在 os.path.isdir sample code is folder / file exist
⭐Python 系統偵測 相關文章整理⭐:
1.【Python】python pyinotify sample code 偵測指定路徑底下的文件變化 (內有範例程式碼)
2.【Python】python 利用 argparse 使程式執行時可帶參數 (內附範例程式碼) sample code
⭐Python 平行運算 相關文章整理⭐:
1.【Python】threading – 建立多執行緒來執行程式 (內含範例程式碼) sample code
2.【Python】multiprocessing – 用多核心來執行程式 (內含範例程式碼) sample code
3.【Python】multiprocessing pool, map, apply_async – 用多核心來執行程式並取得結果 (內含範例程式碼) sample code
4.【Python】python thread multiprocess 比較總整理
5.【Python】python pooling multiprocess – 用多核心來執行程式 sample code (內含範例程式碼)
⭐Python 測試程式 相關文章整理⭐:
1.【Python】python 測試程式 – 利用 doctest 測試 python testcase 的優雅寫法,適用於 leetcode (doctest in function,搭配 function 的用法)
2.【Leetcode】python – 利用 doctest 測試 leetcode python testcase 的優雅寫法 (doctest in class,搭配 class 的用法)
⭐Python Chatbot 相關文章整理⭐:
1.【Chatbot】(全圖文說明) LINE Developers bot 機器人註冊與設定
2.【Chatbot】(全圖文說明) ngrok 本地伺服器設定方法 – LINE bot local server
3.【Chatbot】Dialogflow API 串接 python 的方法 (內含範例程式碼)
4.【Chatbot】[講義分享] 手把手實作line機器人 (linebot API 運用)
⭐Python Google Colab (Colaboratory) 相關文章整理⭐:
1.【Colab】Python colab 上傳檔案的方法 (內含範例程式碼) upload files
2.【Colab】Python colab 連接 google 雲端硬碟取用資料 (內含範例程式碼) mount google drive
⭐Python 其他筆記 相關文章整理⭐:
1.【Python】anaconda 更新 (upgrade) python 3.8 版本筆記
2.【Sublime】Sublime 將縮排 “tab” 改成 4格空白 的方法 (圖文說明) sublime indent 4 spaces
3.【Sublime】Python 縮排小技巧 (很爛但實用) 快速將 tab 改成「4格空白」的方法
⭐【喜歡我的文章嗎? 歡迎幫我按讚~ 讓基金會請創作者喝一杯咖啡!
如果喜歡我的文章,請幫我在下方【按五下Like】 (Google, Facebook 免註冊),會由 「LikeCoin」 贊助作者鼓勵繼續創作,讀者們「只需幫忙按讚,完全不用出錢」哦!

likecoin-steps