【💊 Python的解憂錦囊】如何在multithread/multiprocess傳遞固定參數?

更新於 發佈於 閱讀時間約 11 分鐘
raw-image


撰寫Python的朋友都知道multithread/multiprocess能為我們帶來效能的改進,減少硬體資源的閒置,但在撰寫的過程中常常會發現到我們所設計的工作池模式會需要將「待辦清單」的工作項目當成參數傳遞進去執行, 除了「待辦清單」之外, 其餘的參數基本上都是固定的, 基於這樣的需求之下, 我們要怎麼完成呢? 讓我們耐心的看完這個篇章。

我們通常會這樣做…

假設我們設計了一個工作但尚未實作工作詳細內容, 僅印出工作資訊如下 :

def job(name: str, action: str, item: str):
"""工作內容

Args :
name (str): 什麼樣的工作
action (str): 工作的行為(加工、蓋房、...)
item: 工作的項目
Retruns:
None
"""
result = f'{name} 正在 {action} {item}'
return result

接著我們在主程序設計好我們要執行「什麼樣的工作」、「工作的行為」, 接著我們會有許多的「待辦事項」需要執行, 接著招聘好工人(num_workers)之後就可以根據作業區擴展廠區(pool),每個作業區獨立運作這些待辦清單, 那我們可能會這樣撰寫程式:


import concurrent.futures

if __name__ == "__main__":
# 設計今天的主題
name = '食品加工廠'
action = '製作'

# 待辦事項
todo_list = ['熱狗', '炸雞', '薯條', '肉乾']

num_workers = 3

# 創建一個多進程池,根據上述的工人數量擴展工作池
with concurrent.futures.ProcessPoolExecutor(max_workers=num_workers) as executor:

# 我們將大量的待辦事項轉換成每個worker需要執行的
params = [(name, action, item) for item in todo_list]
results = executor.map(job, params)

# 印出執行結果
for result in results:
print(result)
raw-image


但上述的作法真的好嗎?

我們的params不會隨著「待辦清單」越多(上千萬個事項), 導致記憶體爆掉嗎? 不妨來看看我們更改後的範例, 假設有1000萬的「待辦清單」時會發生什麼狀況? 我們也順便埋入執行的估測時間來實際看看結果。

import multiprocessing
import time
import random
import sys

def job(name: str, action: str, item: str):
"""工作內容

Args :
name (str): 工作名稱
action (str): 動作
item: 工作的項目
Retruns:
None
"""
secs = random.random()
result = f'{name} 正在 {action} {item} 花費了 {secs} 秒'
time.sleep(secs)
return result

if __name__ == "__main__":
# 固定的參數
name = '食品加工廠'
action = '製作'

# 主程序提供的待辦事項清單
# todo_list = ['熱狗', '炸雞', '薯條', '肉乾']
todo_list = [f"工作{i}" for i in range(1, 10000000)]

num_workers = 3

# 創建一個多進程池,這裡使用3個進程
with multiprocessing.Pool(processes=num_workers) as pool:
start_time = time.time()
params = [(name, action, item) for item in todo_list]
end_time = time.time()

use_bytes = sys.getsizeof(params)
use_mb = use_bytes / 1048576
print(f'渲染參數花費的時間: {end_time - start_time} 秒, 耗用的記憶體: {use_mb} MB')

results = pool.starmap(job, params)
for result in results:
print(result)

我們會發現以下光是簡單的參數就花費如此之多的記憶體耗用量, 那面對大數據時怎麼辦?

raw-image

因此我們可以這樣做…

我們在「【Python 軍火庫🧨 - functools】使用partial來設計函數樣板」有介紹到「functools.partial」這個工具庫, 我們可以利用partial的技巧製造出固定參數的新函式, 以不變應萬變,套用到multiprocess之前就不需要一堆複製的資源耗費…。

那在進入主題之前, 我們先來複習一下關於functools.partial函式, 他可以幫我們製作出固定參數的樣版。

raw-image


但聰明的大家有沒有觀察到一個狀況, 那就是動態的參數通常在前面(a, b), 而固定的參數放在後段(c), 因此底下我們的job的參數設計勢必要改一改, 由於我們的item會隨著todolist而變化, 因此需要將函式參數順序稍微修改一下成「job(item: str, name: str, action: str)」。

另外在於multiprocess的部份, 我們原先使用的是「starmap」接受多參數的模式, 但經上述演示之後覺得對於大數據的處理不太妥當, 因此我們可以更換成「map(func, iterable[, chunksize])」, 他們的差異主要在於

import multiprocessing
import time
import random
import sys
from functools import partial

def job(item: str, name: str, action: str):
"""工作內容

Args :
item (str): 工作的項目 [可變]
name (str): 工作名稱 [固定]
action (str): 動作 [固定]

Retruns:
None
"""
secs = random.random()
result = f'{name} 正在 {action} {item} 花費了 {secs} 秒'
return result

if __name__ == "__main__":
# 固定的參數
name = '食品加工廠'
action = '製作'

# 主程序提供的待辦事項清單
# todo_list = ['熱狗', '炸雞', '薯條', '肉乾']
todo_list = [f"工作{i}" for i in range(1, 10000000)]

num_workers = 24

# 創建一個多進程池,這裡使用3個進程
with multiprocessing.Pool(processes=num_workers) as pool:
start_time = time.time()
job_func = partial(job, name=name, action=action)
end_time = time.time()

use_bytes = sys.getsizeof(job_func)
use_mb = use_bytes / 1048576
print(f'渲染參數花費的時間: {end_time - start_time} 秒, 耗用的記憶體: {use_mb} MB')

results = pool.map(job_func, todo_list)
for result in results:
print(result)
raw-image

乍看之下是不是會誤以為花費更久的時間了呢? 請仔細看一下它的數字是否怪怪的, 有負數的出現…, 這是科學記號, 代表是一個非常小的數字, 也就是幾乎沒有耗損的過程, 這面對於大數據的處理, 相對能夠帶來極大的效能增幅。

結語

上述的例子仍有一些缺陷, 雖然實際上仍是以multiprocess的方式並行處理我們的運算, 但必須等待全部的工作完成後, 才回到主線程, 這對於我們跟蹤程式進度的應用會稍微不利, 不過沒關係, 我們後續也會針對這個部份進行重點分享, 歡迎持續追蹤, 讓我們一起探究軟體開發的大小事。

學習軟體開發的路上常常苦於網路資訊爆炸嗎? 教學何其多,但卻遇到無法明確選擇的困境呢? 歡迎加入「🔒 阿Han的軟體心法實戰營」, 這裡不給您冗餘的雜訊, 單刀直入直接送您業界開發重點, 避開選擇障礙的困境, 讓您獲得業界標準的開發起手式, 成為Top 1的頂尖人才。

avatar-img
119會員
268內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
阿Han的沙龍 的其他內容
我們在「【💊 Python的解憂錦囊】如何將dict轉成json並儲存」有介紹過如何將dict型態的資料轉換成json,除了json之外, 另一個耳熟能詳的資料交換格式就是csv了, 我們常常會將csv讀進來, 並使用預先設計的@dataclass來存放, 如此一來實際運行時, 更能夠貼近於我
關於json的資料交換格式請參考「【程式語言 - Javascript】輕量資料格式 JSON」, 我們常常會在使用套件的過程中發現回傳值的型態都會有「dict」的蹤跡, 為什麼呢? 因為動態、彈性、靈活, 不需要預先定義類別來明確指定每個欄位的內容, 但缺點就是文件必須寫清楚內容物是哪些, 否
「functools.partial」是Python中的一個標準函式庫,它可以讓我們基於既有的函式封裝成多種不同用途的函式,就如同上圖所示,我們設計了一個乘法(multiply)的函數,使用了partial讓函數的參數「c」固定下來依據用途不同變化出「double」、「triple」,這樣一來我
最近正好在研究「silero-vad」這套工具, 但根據官方教學,預設的載入方式會從網路上進行下載模型的動作: model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',
我們在「【💎Python 軍火庫 - devpi】pip install…等太久了嗎🤔? 您需要來點緩存機制」有介紹過pypi套件緩存的架設方式, 那架設好了之後, 我們在下載的部份會有一層快取及代理的前哨站, 但假如我們的套件不在pypi平台時怎麼辦呢? 就像torch的套件就必須仰賴外部的來
相信玩過Python一陣子的朋友應該曾經都遇到過套件版本衝突的問題吧…, 這實在是很惱人, 但如果我們能夠快速的檢驗我們所安裝的套件是否如我們所預期的版本, 該有多好, 如此一來能夠讓自己更加安心一些, 那麼今天將提供兩種方法讓我們來檢查一番。 列出所有套件及版本 簡單且暴力。 pip lis
我們在「【💊 Python的解憂錦囊】如何將dict轉成json並儲存」有介紹過如何將dict型態的資料轉換成json,除了json之外, 另一個耳熟能詳的資料交換格式就是csv了, 我們常常會將csv讀進來, 並使用預先設計的@dataclass來存放, 如此一來實際運行時, 更能夠貼近於我
關於json的資料交換格式請參考「【程式語言 - Javascript】輕量資料格式 JSON」, 我們常常會在使用套件的過程中發現回傳值的型態都會有「dict」的蹤跡, 為什麼呢? 因為動態、彈性、靈活, 不需要預先定義類別來明確指定每個欄位的內容, 但缺點就是文件必須寫清楚內容物是哪些, 否
「functools.partial」是Python中的一個標準函式庫,它可以讓我們基於既有的函式封裝成多種不同用途的函式,就如同上圖所示,我們設計了一個乘法(multiply)的函數,使用了partial讓函數的參數「c」固定下來依據用途不同變化出「double」、「triple」,這樣一來我
最近正好在研究「silero-vad」這套工具, 但根據官方教學,預設的載入方式會從網路上進行下載模型的動作: model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',
我們在「【💎Python 軍火庫 - devpi】pip install…等太久了嗎🤔? 您需要來點緩存機制」有介紹過pypi套件緩存的架設方式, 那架設好了之後, 我們在下載的部份會有一層快取及代理的前哨站, 但假如我們的套件不在pypi平台時怎麼辦呢? 就像torch的套件就必須仰賴外部的來
相信玩過Python一陣子的朋友應該曾經都遇到過套件版本衝突的問題吧…, 這實在是很惱人, 但如果我們能夠快速的檢驗我們所安裝的套件是否如我們所預期的版本, 該有多好, 如此一來能夠讓自己更加安心一些, 那麼今天將提供兩種方法讓我們來檢查一番。 列出所有套件及版本 簡單且暴力。 pip lis
你可能也想看
Google News 追蹤
Thumbnail
現代社會跟以前不同了,人人都有一支手機,只要打開就可以獲得各種資訊。過去想要辦卡或是開戶就要跑一趟銀行,然而如今科技快速發展之下,金融App無聲無息地進到你生活中。但同樣的,每一家銀行都有自己的App時,我們又該如何選擇呢?(本文係由國泰世華銀行邀約) 今天我會用不同角度帶大家看這款國泰世華CUB
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
在現今快節奏的生活中,多工作業常被視為提高效率的方式。然而,大腦無法同時處理多個需要認知能力的任務,只能進行任務切換。研究顯示,多工反而使得每項任務的效率降低,因此,專注於單一任務可能更能提升生活和工作的效率,讓我們擺脫多工迷思吧!
Thumbnail
在流程控制中,最常用的就是for loop 或是 while loop 語法了。 最常見的場景就是根據條件判斷式,重複執行特定的指令。 如果要在python寫出類似C/C++ for loop,可以怎麼寫呢? 透過索引去進行迭代 for var in range( start=0, sto
Thumbnail
本文介紹了Python中的流程控制,包括if, elif, else語句,三元運算子,for和while迴圈,以及控制迴圈語句如break、continue和pass。透過範例程式碼,說明了如何使用這些語句和結構進行條件判斷,迴圈遍歷和控制程式流程。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
本文介紹了Python中函式引數的*args和**kwargs用法,通過*args處理可變數量的位置引數,通過**kwargs處理可變數量的關鍵字引數。不僅介紹了相應的語法和程式範例,還解釋了它們的順序問題和建議的慣例用法。
Thumbnail
在日常中,常有重複性相當高的事情,不斷地重複在做,重複的事做久就會慢慢變成是一個習慣,這個習慣就會讓人下意識地完成一些事情。 習慣是一種自動化的行為模式,這些行為模式在重複進行的過程中變得固定且容易自動化。 在Python程式語言中,for迴圈就類似這種概念
Thumbnail
列表(List)和元組(Tuple)都是 Python 中用來存儲集合元素的數據結構,兩者看起來很像,在初學時很容易搞混,所以觀念要建立好。 可以把列表(List)和元組(Tuple)想像成是一個容器,什麼元素都可以塞
Thumbnail
現代社會跟以前不同了,人人都有一支手機,只要打開就可以獲得各種資訊。過去想要辦卡或是開戶就要跑一趟銀行,然而如今科技快速發展之下,金融App無聲無息地進到你生活中。但同樣的,每一家銀行都有自己的App時,我們又該如何選擇呢?(本文係由國泰世華銀行邀約) 今天我會用不同角度帶大家看這款國泰世華CUB
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
在現今快節奏的生活中,多工作業常被視為提高效率的方式。然而,大腦無法同時處理多個需要認知能力的任務,只能進行任務切換。研究顯示,多工反而使得每項任務的效率降低,因此,專注於單一任務可能更能提升生活和工作的效率,讓我們擺脫多工迷思吧!
Thumbnail
在流程控制中,最常用的就是for loop 或是 while loop 語法了。 最常見的場景就是根據條件判斷式,重複執行特定的指令。 如果要在python寫出類似C/C++ for loop,可以怎麼寫呢? 透過索引去進行迭代 for var in range( start=0, sto
Thumbnail
本文介紹了Python中的流程控制,包括if, elif, else語句,三元運算子,for和while迴圈,以及控制迴圈語句如break、continue和pass。透過範例程式碼,說明了如何使用這些語句和結構進行條件判斷,迴圈遍歷和控制程式流程。
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
Thumbnail
在Python中,queue是一個非常有用的模块。 它提供了多種佇列(queue)實現,用於在多線程環境中安全地交換信息或者數據。 佇列(queue)是一種先進先出(FIFO)的數據結構,允許在佇列的一端插入元素,另一端取出元素。(FIFO 是First In, First Out 的縮寫)
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
本文介紹了Python中函式引數的*args和**kwargs用法,通過*args處理可變數量的位置引數,通過**kwargs處理可變數量的關鍵字引數。不僅介紹了相應的語法和程式範例,還解釋了它們的順序問題和建議的慣例用法。
Thumbnail
在日常中,常有重複性相當高的事情,不斷地重複在做,重複的事做久就會慢慢變成是一個習慣,這個習慣就會讓人下意識地完成一些事情。 習慣是一種自動化的行為模式,這些行為模式在重複進行的過程中變得固定且容易自動化。 在Python程式語言中,for迴圈就類似這種概念
Thumbnail
列表(List)和元組(Tuple)都是 Python 中用來存儲集合元素的數據結構,兩者看起來很像,在初學時很容易搞混,所以觀念要建立好。 可以把列表(List)和元組(Tuple)想像成是一個容器,什麼元素都可以塞