python線程池內存耗盡,python進程池內存一直上漲

前言

大家好,我是星期八。

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

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

所以通常情況下,我們會引出線程池這個概念。

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

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

開心一刻

理想的多線程

一篇文章淺析Python自帶的線程池和進程池

實際的多線程

一篇文章淺析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執行完畢")

執行結果

一篇文章淺析Python自帶的線程池和進程池

發現的問題

其實這個就是並發的,不要懷疑,但是你有沒有發現個問題,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()。

執行結果

一篇文章淺析Python自帶的線程池和進程池

這次結果就是我們想要的了,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自帶的線程池和進程池

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

多進程方式

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

但是都知道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()。

基本這幾個都夠自己用了。

如果在操作過程中有任何問題,記得下面留言,我們看到會第一時間解決問題。

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/228125.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-09 21:31
下一篇 2024-12-09 21:31

相關推薦

發表回復

登錄後才能評論