程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python 中如何快速實現一個線程池? 最全(1)

編輯:Python

當有多個 IO 密集型的任務要被處理時,我們自然而然會想到多線程。但如果任務非常多,我們不可能每一個任務都啟動一個線程去處理,這個時候最好的辦法就是實現一個線程池,至於池子裡面的線程數量可以根據業務場景進行設置。

比如我們實現一個有 10 個線程的線程池,這樣可以並發地處理 10 個任務,每個線程將任務執行完之後,便去執行下一個任務。通過使用線程池,可以避免因線程創建過多而導致資源耗盡,而且任務在執行時的生命周期也可以很好地把控。

而線程池的實現方式也很簡單,但這裡我們不打算手動實現,因為 Python 提供了一個標准庫 concurrent.futures,已經內置了對線程池的支持。所以本篇文章,我們就來詳細介紹一下該模塊的用法。

Future 對象

當我們往線程池裡面提交一個函數時,會分配一個線程去執行,同時立即返回一個 Future 對象。通過 Future 對象可以監控函數的執行狀態,有沒有出現異常,以及有沒有執行完畢等等。如果函數執行完畢,內部便會調用 future.set_result 將返回值設置到 future 裡面,然後外界便可調用 future.result 拿到返回值。

除此之外 future 還可以綁定回調,一旦函數執行完畢,就會以 future 為參數,自動觸發回調。所以 future 被稱為未來對象,可以把它理解為函數的一個容器,當我們往線程池提交一個函數時,會立即創建相應的 future 然後返回。函數的執行狀態什麼的,都通過 future 來查看,當然也可以給它綁定一個回調,在函數執行完畢時自動觸發。

那麼下面我們就來看一下 future 的用法,文字的話理解起來可能有點枯燥。

 需要注意的是:只能執行一次 set_result,但是可以多次調用 result 獲取結果。

 執行 future.result() 之前一定要先 set_result,否則會一直處於阻塞狀態。當然 result 方法還可以接收一個 timeout 參數,表示超時時間,如果在指定時間內沒有獲取到值就會拋出異常。

提交函數自動創建 Future 對象

我們上面是手動創建的 Future 對象,但工作中很少會手動創建。我們將函數提交到線程池裡面運行的時候,會自動創建 Future 對象並返回。這個 Future 對象裡面就包含了函數的執行狀態,比如此時是處於暫停、運行中還是完成等等,並且函數在執行完畢之後,還會調用 future.set_result 將自身的返回值設置進去。

 

 

 這裡再強調一下 future.result(),這一步是會阻塞的,舉個例子:

可以看到,future.result() 這一步花了將近 3s。其實也不難理解,future.result() 是干嘛的?就是為了獲取函數的返回值,可函數都還沒有執行完畢,它又從哪裡獲取呢?所以只能先等待函數執行完畢,將返回值通過 set_result 設置到 future 裡面之後,外界才能調用 future.result() 獲取到值。

如果不想一直等待的話,那麼在獲取值的時候可以傳入一個超時時間。

 當然啦,這麼做其實還不夠智能,因為我們不知道函數什麼時候執行完畢。所以最好的辦法還是綁定一個回調,當函數執行完畢時,自動觸發回調。

需要注意的是,在調用 submit 方法之後,提交到線程池的函數就已經開始執行了。而不管函數有沒有執行完畢,我們都可以給對應的 future 綁定回調。

如果函數完成之前添加回調,那麼會在函數完成後觸發回調。如果函數完成之後添加回調,由於函數已經完成,代表此時的 future 已經有值了,或者說已經 set_result 了,那麼會立即觸發回調。

future.set_result 到底干了什麼事情

當函數執行完畢之後,會執行 set_result,那麼這個方法到底干了什麼事情呢?

我們看到 future 有兩個被保護的屬性,分別是 _result 和 _state。顯然 _result 用於保存函數的返回值,而 future.result() 本質上也是返回 _result 屬性的值。而 _state 屬性則用於表示函數的執行狀態,初始為 PENDING,執行中為 RUNING,執行完畢時被設置為 FINISHED。

調用 future.result() 的時候,會判斷 _state 的屬性,如果還在執行中就一直等待。當 _state 為 FINISHED 的時候,就返回 _result 屬性的值。

提交多個函數

我們上面每次只提交了一個函數,但其實可以提交任意多個,我們來看一下:

 如果是多個函數,要如何拿到返回值呢?很簡單,遍歷 futures 即可。

這裡面有一些值得說一說的地方,首先 futures 裡面有 5 個 future,記做 future1, future2, future3, future4, future5。

當使用 for 循環遍歷的時候,實際上會依次遍歷這 5 個 future,所以返回值的順序就是我們添加的函數的順序。由於 future1 對應的函數休眠了 5s,那麼必須等到 5s 後,future1 裡面才會有值。

但這五個函數是並發執行的,future2, future3, future4 由於只休眠了 2s, 4s, 3s,所以肯定會先執行完畢,然後執行 set_result,將返回值設置到對應的 future 裡。

但 Python 的 for 循環不可能在第一次迭代還沒有結束,就去執行第二次迭代。因為 futures 裡面的幾個 future 的順序已經一開始就被定好了,只有當第一個 future.result() 執行完成之後,才會執行第二個 future.result(),以及第三個、第四個。

因此即便後面的函數已經執行完畢,但由於 for 循環的順序,也只能等著,直到前面的 future.result() 執行完畢。所以當第一個 future.result() 結束時,後面三個 future.result() 會立刻輸出,因為它們內部的函數已經執行結束了。

而最後一個 future,由於內部函數 sleep 了 6 秒,因此要再等待 1 秒,才會打印 future.result()。

【學習交流群853991558】

【網盤免費資料包,大家感興趣的可以看一下】:

Python全網最熱資料包【華清遠見發放資料包】http://makerschool.mikecrm.com/6cvAGKm

【下方分享一些免費學習教程,大家感興趣的可以看一下】:

 python入門

Python的封裝

Python網絡編程

python高級用法

Python爬蟲開發

python 人工智能-神經網絡

django項目案例精講

人工智能面試題解答

雲計算 大數據和人工智能

快速學習爬蟲基礎


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved