首頁 > 科技

一篇文章淺析Python自帶的執行緒池和程序池

2021-06-11 15:30:28

來源:Python爬蟲與資料探勘

作者:星期八

前言

大家好,我是星期八。

我們都知道,不管是Java,還是C++,還是Go,還是Python,都是有執行緒這個概念的。

但是我們知道,執行緒是不能隨便創建的,就像每招一個員工一樣,是有代價的,無限制招人肯定最後各種崩潰。

所以通常情況下,我們會引出執行緒池這個概念。

本質就是我就招了幾個固定的員工,給他們派活,某一個人的活幹完了再去任務中心領取新的活。

防止任務太多,一次性招太多工人,最後系統崩潰。

開心一刻

理想的多執行緒

實際的多執行緒

from concurrent.futures import ...

可能也是因為執行緒池這個東西用的越來越多了吧,從Python3.2+之後,就成了內建模組

對的,直接就能使用,不需要pip進行安裝什麼的。

concurrent.futures下面主要有倆介面。

ThreadPoolExecutor

執行緒池。ProcessPoolExecutor程序池。這裡可沒有什麼所謂的非同步池。

個人看法:雖然非同步的效能很高,但是目前除了Go以外,其他實現的都不是太好,用法上面有些怪異,當然,你們可以說我菜,我承認。

執行緒池

示例程式碼

import time

from concurrent.futures import ThreadPoolExecutor

import random

# max_workers表示工人數量,也就是執行緒池裡面的執行緒數量

pool = ThreadPoolExecutor(max_workers=10)

# 任務列表

task_list = ["任務1", "任務2", "任務3", "任務4", ]

def handler(task_name):

# 隨機睡眠,模仿任務執行時間不確定性

n = random.randrange(5)

time.sleep(n)

print(f"任務內容:{task_name}")

if __name__ == '__main__':

# 遍歷任務,

for task in task_list:

"""

交給函數處理,submit會將所有任務都提交到一個地方,不會阻塞

然後執行緒池裡面的每個執行緒會來取任務,

比如:執行緒池有3個執行緒,但是有5個任務

會先取走三個任務,每個執行緒去處理

其中一個執行緒處理完自己的任務之後,會再來提交過的任務區再拿走一個任務

"""

pool.submit(handler, task)

print("main執行完畢")

執行結果

發現的問題

其實這個就是併發的,不要懷疑,但是你有沒有發現個問題,main先執行,這說明啥?

這說明,我main跑完之後,是不管子執行緒的死活的。

那能不能設定一下,所有的子執行緒都執行完之後main函數在執行完?

當然可以,需要一個參數即可。

pool.shutdown()

要完成上述的問題,我們需要一個參數,加上這個參數之後。

就可以讓主執行緒等待所有子執行緒執行完之後,主執行緒再執行完

示例程式碼

...if __name__ == '__main__':# 遍歷任務, for task in task_list: """ 交給函數處理,submit會將所有任務都提交到一個地方 然後執行緒池裡面的每個執行緒會來取任務, 比如:執行緒池有3個執行緒,但是有5個任務 會先取走三個任務,每個執行緒去處理 其中一個執行緒處理完自己的任務之後,會再來提交過的任務區再拿走一個任務 """ pool.submit(handler, task) pool.shutdown() print("main執行完畢")主要就是13行的pool.shutdown()執行結果

這次結果就是我們想要的了,hhh!!!

add_done_callback

add_done_callback

可以理解為是回撥函數,執行緒執行完之後,會自動呼叫指定的回撥函數。

並且能拿到執行緒執行函數的返回值

有什麼用,我也沒用過,怪我才疏學淺叭。

示例程式碼

import time

from concurrent.futures import ThreadPoolExecutor

import random

from concurrent.futures._base import Future

# max_workers表示工人數量,也就是執行緒池裡面的執行緒數量

pool = ThreadPoolExecutor(max_workers=10)

# 任務列表

task_list = ["任務1", "任務2", "任務3", "任務4", ]

def handler(task_name):

# 隨機睡眠,模仿任務執行時間不確定性

n = random.randrange(5)

time.sleep(n)

print(f"任務內容:{task_name}")

return f"任務內容:{task_name}"

def done(res: Future):

print("done拿到的返回值:", res.result())

if __name__ == '__main__':

# 遍歷任務,

for task in task_list:

futrue = pool.submit(handler, task) # type:Future

futrue.add_done_callback(done)

pool.shutdown()

print("main執行完畢")

注意:第17,27,28行程式碼!

執行效果

我想,可能通常用在一些善後工作叭。

多程序方式

其實通過上述幾個例子,我們基本是知道怎麼使用上面這個執行緒池了。

但是都知道Python的執行緒,因為GIL(全局直譯器鎖)的原因,是不能併發到多個物理核心上的。

所以是IO密集型的,像爬蟲,讀寫檔案,使用執行緒池是ok的。

但是如果說我就是野,就是頭鐵,非要用Python做計算型應用,像圖片壓縮、視訊流推送,那沒辦法,需要使用多程序池方式。

其實通過concurrent這個介面,可以很方便的創建程序池,只需要修改兩個地方。...

# 改成匯入程序池方式

from concurrent.futures import ProcessPoolExecutor

...

if __name__ == '__main__':

...

# 程序池方式

pool = ProcessPoolExecutor(max_workers=10)

...

只需要修改這倆地方即可,其他和上述用法一摸一樣。

總結

本篇主要講的是Python自帶的執行緒池程序池

比較有特色的是,

ThreadPoolExecutor,ProcessPoolExecutor的介面是一樣的。只需要修改匯入的包就行。concurrent的介面主要有pool.submit(),pool.shutdown(),futrue.add_done_callback()。基本這幾個都夠自己用了。


IT145.com E-mail:sddin#qq.com