python運行原理_Python線程池及其原理和使用(超級詳細)

系統啟動一個新線程的成本是比較高的,因為它涉及與操作系統的交互。在這種情形下,使用線程池可以很好地提升性能,尤其是當程序中需要創建大量生存期很短暫的線程時,更應該考慮使用線程池。

線程池在系統啟動時即創建大量空閑的線程,程序只要將一個函數提交給線程池,線程池就會啟動一個空閑的線程來執行它。當該函數執行結束后,該線程并不會死亡,而是再次返回到線程池中變成空閑狀態,等待執行下一個函數。

此外,使用線程池可以有效地控制系統中并發線程的數量。當系統中包含有大量的并發線程時,會導致系統性能急劇下降,甚至導致 Python 解釋器崩潰,而線程池的最大線程數參數可以控制系統中并發線程的數量不超過此數。

線程池的使用

線程池的基類是 concurrent.futures 模塊中的 Executor,Executor 提供了兩個子類,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于創建線程池,而 ProcessPoolExecutor 用于創建進程池。

如果使用線程池/進程池來管理并發編程,那么只要將相應的 task 函數提交給線程池/進程池,剩下的事情就由線程池/進程池來搞定。

Exectuor 提供了如下常用方法:

submit(fn, *args, **kwargs):將 fn 函數提交給線程池。*args 代表傳給 fn 函數的參數,*kwargs 代表以關鍵字參數的形式為 fn 函數傳入參數。

map(func, *iterables, timeout=None, chunksize=1):該函數類似于全局函數 map(func, *iterables),只是該函數將會啟動多個線程,以異步方式立即對 iterables 執行 map 處理。

shutdown(wait=True):關閉線程池。

程序將 task 函數提交(submit)給線程池后,submit 方法會返回一個 Future 對象,Future 類主要用于獲取線程任務函數的返回值。由于線程任務會在新線程中以異步方式執行,因此,線程執行的函數相當于一個“將來完成”的任務,所以 Python 使用 Future 來代表。

實際上,在 Java 的多線程編程中同樣有 Future,此處的 Future 與 Java 的 Future 大同小異。

Future 提供了如下方法:

cancel():取消該 Future 代表的線程任務。如果該任務正在執行,不可取消,則該方法返回 False;否則,程序會取消該任務,并返回 True。

cancelled():返回 Future 代表的線程任務是否被成功取消。

running():如果該 Future 代表的線程任務正在執行、不可被取消,該方法返回 True。

done():如果該 Funture 代表的線程任務被成功取消或執行完成,則該方法返回 True。

result(timeout=None):獲取該 Future 代表的線程任務最后返回的結果。如果 Future 代表的線程任務還未完成,該方法將會阻塞當前線程,其中 timeout 參數指定最多阻塞多少秒。

exception(timeout=None):獲取該 Future 代表的線程任務所引發的異常。如果該任務成功完成,沒有異常,則該方法返回 None。

add_done_callback(fn):為該 Future 代表的線程任務注冊一個“回調函數”,當該任務成功完成時,程序會自動觸發該 fn 函數。

在用完一個線程池后,應該調用該線程池的 shutdown() 方法,該方法將啟動線程池的關閉序列。調用 shutdown() 方法后的線程池不再接收新任務,但會將以前所有的已提交任務執行完成。當線程池中的所有任務都執行完成后,該線程池中的所有線程都會死亡。

使用線程池來執行線程任務的步驟如下:

調用 ThreadPoolExecutor 類的構造器創建一個線程池。

定義一個普通函數作為線程任務。

調用 ThreadPoolExecutor 對象的 submit() 方法來提交線程任務。

當不想提交任何任務時,調用 ThreadPoolExecutor 對象的 shutdown() 方法來關閉線程池。

下面程序示范了如何使用線程池來執行線程任務:

from concurrent.futures import ThreadPoolExecutor

import threading

import time

# 定義一個準備作為線程任務的函數

def action(max):

my_sum = 0

for i in range(max):

print(threading.current_thread().name + ' ' + str(i))

my_sum += i

return my_sum

# 創建一個包含2條線程的線程池

pool = ThreadPoolExecutor(max_workers=2)

# 向線程池提交一個task, 50會作為action()函數的參數

future1 = pool.submit(action, 50)

# 向線程池再提交一個task, 100會作為action()函數的參數

future2 = pool.submit(action, 100)

# 判斷future1代表的任務是否結束

print(future1.done())

time.sleep(3)

# 判斷future2代表的任務是否結束

print(future2.done())

# 查看future1代表的任務返回的結果

print(future1.result())

# 查看future2代表的任務返回的結果

print(future2.result())

# 關閉線程池

pool.shutdown()

上面程序中,第 13 行代碼創建了一個包含兩個線程的線程池,接下來的兩行代碼只要將 action() 函數提交(submit)給線程池,該線程池就會負責啟動線程來執行 action() 函數。這種啟動線程的方法既優雅,又具有更高的效率。

當程序把 action() 函數提交給線程池時,submit() 方法會返回該任務所對應的 Future 對象,程序立即判斷 futurel 的 done() 方法,該方法將會返回 False(表明此時該任務還未完成)。接下來主程序暫停 3 秒,然后判斷 future2 的 done() 方法,如果此時該任務已經完成,那么該方法將會返回 True。

程序最后通過 Future 的 result() 方法來獲取兩個異步任務返回的結果。

讀者可以自己運行此代碼查看運行結果,這里不再演示。

當程序使用 Future 的 result() 方法來獲取結果時,該方法會阻塞當前線程,如果沒有指定 timeout 參數,當前線程將一直處于阻塞狀態,直到 Future 代表的任務返回。

獲取執行結果

前面程序調用了 Future 的 result() 方法來獲取線程任務的運回值,但該方法會阻塞當前主線程,只有等到錢程任務完成后,result() 方法的阻塞才會被解除。

如果程序不希望直接調用 result() 方法阻塞線程,則可通過 Future 的 add_done_callback() 方法來添加回調函數,該回調函數形如 fn(future)。當線程任務完成后,程序會自動觸發該回調函數,并將對應的 Future 對象作為參數傳給該回調函數。

下面程序使用 add_done_callback() 方法來獲取線程任務的返回值:

from concurrent.futures import ThreadPoolExecutor

import threading

import time

# 定義一個準備作為線程任務的函數

def action(max):

my_sum = 0

for i in range(max):

print(threading.current_thread().name + ' ' + str(i))

my_sum += i

return my_sum

# 創建一個包含2條線程的線程池

with ThreadPoolExecutor(max_workers=2) as pool:

# 向線程池提交一個task, 50會作為action()函數的參數

future1 = pool.submit(action, 50)

# 向線程池再提交一個task, 100會作為action()函數的參數

future2 = pool.submit(action, 100)

def get_result(future):

print(future.result())

# 為future1添加線程完成的回調函數

future1.add_done_callback(get_result)

# 為future2添加線程完成的回調函數

future2.add_done_callback(get_result)

print('--------------')

上面主程序分別為 future1、future2 添加了同一個回調函數,該回調函數會在線程任務結束時獲取其返回值。

主程序的最后一行代碼打印了一條橫線。由于程序并未直接調用 future1、future2 的 result() 方法,因此主線程不會被阻塞,可以立即看到輸出主線程打印出的橫線。接下來將會看到兩個新線程并發執行,當線程任務執行完成后,get_result() 函數被觸發,輸出線程任務的返回值。

另外,由于線程池實現了上下文管理協議(Context Manage Protocol),因此,程序可以使用 with 語句來管理線程池,這樣即可避免手動關閉線程池,如上面的程序所示。

此外,Exectuor 還提供了一個 map(func, *iterables, timeout=None, chunksize=1) 方法,該方法的功能類似于全局函數 map(),區別在于線程池的 map() 方法會為 iterables 的每個元素啟動一個線程,以并發方式來執行 func 函數。這種方式相當于啟動 len(iterables) 個線程,井收集每個線程的執行結果。

例如,如下程序使用 Executor 的 map() 方法來啟動線程,并收集線程任務的返回值:

from concurrent.futures import ThreadPoolExecutor

import threading

import time

# 定義一個準備作為線程任務的函數

def action(max):

my_sum = 0

for i in range(max):

print(threading.current_thread().name + ' ' + str(i))

my_sum += i

return my_sum

# 創建一個包含4條線程的線程池

with ThreadPoolExecutor(max_workers=4) as pool:

# 使用線程執行map計算

# 后面元組有3個元素,因此程序啟動3條線程來執行action函數

results = pool.map(action, (50, 100, 150))

print('--------------')

for r in results:

print(r)

上面程序使用 map() 方法來啟動 3 個線程(該程序的線程池包含 4 個線程,如果繼續使用只包含兩個線程的線程池,此時將有一個任務處于等待狀態,必須等其中一個任務完成,線程空閑出來才會獲得執行的機會),map() 方法的返回值將會收集每個線程任務的返回結果。

運行上面程序,同樣可以看到 3 個線程并發執行的結果,最后通過 results 可以看到 3 個線程任務的返回結果。

通過上面程序可以看出,使用 map() 方法來啟動線程,并收集線程的執行結果,不僅具有代碼簡單的優點,而且雖然程序會以并發方式來執行 action() 函數,但最后收集的 action() 函數的執行結果,依然與傳入參數的結果保持一致。也就是說,上面 results 的第一個元素是 action(50) 的結果,第二個元素是 action(100) 的結果,第三個元素是 action(150) 的結果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370704.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370704.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370704.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Google Guava緩存

這篇文章是我在Google Guava上系列文章的續篇,這次涵蓋了Guava Cache。 與HashMap或ConcurrentHashMap相比,Guava Cache提供了更大的靈活性和功能,但不像使用EHCache或Memcached那樣繁重(就此而言,它很健壯&#xff0c…

html 三列布局(兩列自適應,一列固定寬度)

不做過多解釋:主要是記錄一個完整的布局樣式,實現頁面大致三列其中左右兩列是自適應寬度,中間固定寬度效果。 不多少代碼奉上: CSS樣式代碼: /*********************公共標簽樣式********************//************…

jsp常用動作

jsp:include 動態包含; jsp:forward 轉發; jsp:useBean 實例化bean對象; jsp:setProperty 設置一個屬性值 jsp:getProperty 獲取一個屬性值 jsp:param 動態傳參數; jsp:plugin 生成一個插件 jsp:useBean 實例化一個對象…

單曲循環 翻譯_歌單 | 單曲循環amp;熱評

December2020/12/ 寫在前面的話 /本來打算在跨年的時候才更文,但是吧又覺得空出這最后一個月有點蒼白,然后最近一直夜半網抑云(敏感ing)就想到可以做一期分享歌單的推文,分享一些最近聽得頻繁的歌曲(還不是刷抖音刷出來的)。《曖昧》// 王菲徘…

python的字符串內建函數

python的字符串內建函數 字符串方法是從python1.6到2.0慢慢加進來的——它們也被加到了Jython中。 這些方法實現了string模塊的大部分方法,如下表所示列出了目前字符串內建支持的方法,所有的方法都包含了對Unicode的支持,有一些甚至是專門用…

休息使用Jersey –包含JAXB,異常處理和客戶端程序的完整教程

最近,我開始使用Jersey API開發一個Restful Web服務項目。 在線提供了一些教程,但是我遇到了異常處理方面的一些問題,而且在使用JaxB和提供異常處理方法的完整項目中找不到任何地方。 因此,一旦我能夠使用帶有異常處理和客戶端程序…

python基于web可視化_獨家 | 基于Python實現交互式數據可視化的工具(用于Web)

轉自:數據派ID:datapi 作者:Alark Joshi 翻譯:陳雨琳 校對:吳金笛 本文2200字,建議閱讀8分鐘。 本文將介紹實現數據可視化的軟件包。 這學期(2018學年春季學期)我教授了一門關于數據…

SASS簡介及使用方法

一、什么是Sass Sass (Syntactically Awesome StyleSheets)是css的一個擴展開發工具,它允許你使用變量、條件語句等,使開發更簡單可維護。這里是官方文檔。 二、基本語法 1)變量 sass的變量名必須是一個$符號開頭,后面緊跟變量名…

【轉】Java方向如何準備BAT技術面試答案(匯總版)

原文地址:http://www.jianshu.com/p/1f1d3193d9e3 這個主題的內容之前分三個篇幅分享過,導致網絡上傳播的比較分散,所以本篇做了一個匯總,同時對部分內容及答案做了修改,歡迎朋友們吐槽、轉發。因為篇幅長度和時間的原…

numpy維度交換_“lazy”的transpose()函數——從numpy 數組的內存布局講起

1 數組的兩種內存布局方式行優先與列優先首先我們回顧一下,矩陣數據在內存中的兩種布局方式:行優先(row-major):以行為優先單位,在內存中逐行存儲/讀取;對于多維,意味著當線性掃描內…

云耀服務器切換系統,【計算】云耀服務器-常見操作匯總指南

通過上期的介紹,相信大家對于云耀云服務器的基本知識有了一個了解。云耀云服務器是一個具備獨立、完整的操作系統和網絡功能,可快速搭建簡單應用的新一代云服務器。接下來,本期為大家帶來關于云耀云服務器使用中的一些簡單方法和小技巧。1.云…

機器學習應該準備哪些數學預備知識?

轉 https://www.zhihu.com/question/36324957 https://www.zhihu.com/question/36324957/answer/139408269 機器學習應該準備哪些數學預備知識? 數據分析師,工作中經常使用機器學習模型,但是以調庫為主。 自己一直也在研究算法,也…

react usecontext_Vue3原理實戰運用,我用40行代碼把他裝進了React做狀態管理

前言vue-next是Vue3的源碼倉庫,Vue3采用lerna做package的劃分,而響應式能力vue/reactivity被劃分到了單獨的一個package中。如果我們想把它集成到React中,可行嗎?來試一試吧。使用示例話不多說,先看看怎么用的解解饞吧…

Spring MVC –自定義RequestMappingHandlerMapping

在xml bean定義文件中使用<mvc&#xff1a;annotation-driven />配置Spring MVC時&#xff0c;在內部將一個名為RequestMappingHandlerMapping的組件注冊到Spring MVC。 該組件或通常是HandlerMapping組件負責將請求URI路由到處理程序&#xff0c;這些處理程序是使用Requ…

css的三個特性 背景透明設置

關于行內元素&#xff08;補充一點&#xff09; 行內元素只能容納文本或其他行內元素。&#xff08;a特殊a里面可以放塊級元素&#xff09; 例子&#xff1a; 關于行高tip: 選擇器的嵌套層級不應大于3級&#xff0c;位置靠后的限定條件應盡可能的精確。 屬性定義必須另起一行…

比較容易犯的一些智障錯誤(不定時修改)

無論在什么學習中&#xff0c;在成長的過程中&#xff0c;注定要犯一些錯誤&#xff0c;有些比較高級的錯誤&#xff0c;有些是比較智障的錯誤。那么在oi的學習中&#xff0c;我們最討厭的就是一些智障的小錯誤&#xff0c;因為如果是大錯誤的話一般情況下在測試樣例的時候都是…

ccs安裝多版本編譯器離線_大數據分析:學習工具JDK,在線安裝指南

hadoop是使用Java語言開發的并且Hadoop運行需要有Java環境的支持&#xff0c;因此在安裝hadoop之前需要安裝Java開發環境即JDK(Java Development Kit)。安裝前首先向大家介紹以一下本文會用到的幾個詞&#xff1a;JAVA_HOME:一是為了方便引用&#xff0c;比如&#xff0c;JDK安…

HTML基礎入門學習準備篇

在學習前端的開始&#xff0c;讓我們一起來了解什么是HTML5時代的大前端開發和全棧開發的定義傳統的前端&#xff1a;切圖-標簽和樣式-實現效果H5時代的前端&#xff1a;一、需要各端的兼容開發二、可以用于APP開發和移動站點的開發三、Ajax服務器端技術開發四、高級設計模式和…

asp.net尚未在web服務器上注冊_最新版Web服務器項目詳解 00 項目概述

點 擊 關 注 上 方&#xff02;兩猿社&#xff02;設 為&#xff02;置 頂 或 星 標&#xff02;&#xff0c;干 貨 第 一 時 間 送 達。互 聯 網 猿 | 兩 猿 社TineyWebServerLinux下C輕量級Web服務器&#xff0c;助力初學者快速實踐網絡編程&#xff0c;搭建屬于自己的服務器…

python正則r的作用_Python正則表達式,這一篇就夠了!

原標題&#xff1a;Python正則表達式&#xff0c;這一篇就夠了&#xff01;大多數編程語言的正則表達式設計都師從Perl&#xff0c;所以語法基本相似&#xff0c;不同的是每種語言都有自己的函數去支持正則&#xff0c;今天我們就來學習 Python中關于 正則表達式的函數。re模塊…