網(wǎng)站首頁 編程語言 正文
線程池
線程池的創(chuàng)建 - concurrent
concurrent
?是 Python 的內(nèi)置包,使用它可以幫助我們完成創(chuàng)建線程池的任務(wù)。
方法名 | 介紹 | 示例 |
---|---|---|
futures.ThreadPoolExecutor | 創(chuàng)建線程池 | tpool=ThreadPoolExecutor(max_workers) |
通過調(diào)用 concurrent 包的 futures 模塊的 ThreadPoolExecutor 類,通過實例化 ThreadPoolExecutor 實現(xiàn)創(chuàng)建線程池的對象,它有一個參數(shù)來設(shè)置 線程池的數(shù)量。這和創(chuàng)建進程池設(shè)置的數(shù)量是完全相同的。
線程池的常用方法
接下里看一下線程池對象中都有哪些常用的方法 :
函數(shù)名 | 介紹 | 用法 |
---|---|---|
submit | 往線程池中添加任務(wù) | submit(target, args) |
done | 確認線程池中的某個線程是否完成了任務(wù) | done() |
rsult | 獲取當前線程執(zhí)行任務(wù)的結(jié)果 | result() |
- submit 函數(shù):通過 submit 函數(shù)將參數(shù)傳入;該函數(shù)傳入的參數(shù)也是傳入要執(zhí)行的函數(shù)與該函數(shù)的參數(shù),由于它的參數(shù)并不用需要通過賦值語句的形式傳入,只需要把相應(yīng)的值傳入就可以了(稍后會進行一個練習)。
- done 函數(shù):判斷當前線程是否執(zhí)行完成;返回值是 bool 類型。
- result 函數(shù):返回當前線程池中線程任務(wù)的執(zhí)行結(jié)果,通過這種方法就可以獲取線程池的返回值了。
線程池演示案例
1、定義一個函數(shù)實現(xiàn)循環(huán)的效果
2、定義一個線程池,設(shè)置線程的數(shù)量
# coding:utf-8 import time from concurrent.futures import ThreadPoolExecutor def work(i): print('第 {} 次循環(huán)'.format(i)) time.sleep(1) # 之所以每次都要使用 sleep 函數(shù),是因為函數(shù)執(zhí)行太快;通過 sleep 嘗試模擬一下長時間的執(zhí)行一個任務(wù) if __name__ == '__main__': thread_poor = ThreadPoolExecutor(4) # 實例化一個線程池,設(shè)置線程數(shù)量為4 for i in range(20): thread_poor.submit(work, (i,)) # 利用 submit 函數(shù)將任務(wù)添加至 work 函數(shù)
運行效果如下?
從運行結(jié)果來看,我們的線程任務(wù)每次執(zhí)行4個任務(wù),阻塞一秒后再執(zhí)行后續(xù)的四個線程的任務(wù),是沒有問題的。
PS:需要注意的是,運行結(jié)果有可能是出現(xiàn)將兩個或者多個任務(wù)的結(jié)果在同一行打印輸出,這是因為在同一時間處理了多個線程的任務(wù),這也叫 "并發(fā)"。
線程鎖
前文的進程池是與進程鎖相對應(yīng)匹配的,同樣的線程池也有與之對應(yīng)的 線程鎖 。線程鎖的使用方法幾乎與進程鎖是一樣的,只不過線程鎖對應(yīng)的是線程罷了。
1.實例化一個線程鎖
2、在 work 函數(shù)中調(diào)用線程鎖
3、并獲取?線程?的返回值(線程池也是可以獲取返回值的)
代碼示例如下:
# coding:utf-8 import os import time import threading from concurrent.futures import ThreadPoolExecutor lock = threading.Lock() # 全局定義一個 Lock() 實例 def work(i): lock.acquire() # 區(qū)別于 進程鎖 只需要在全局實例化一個即可,線程鎖需要在線程任務(wù)的函數(shù)中調(diào)用 線程鎖 才會生效 print('當前是第 {} 次循環(huán)'.format(i)) time.sleep(1) # 之所以每次都要使用 sleep 函數(shù),是因為函數(shù)執(zhí)行太快;通過 sleep 嘗試模擬一下長時間的執(zhí)行一個任務(wù) lock.release() return '第 {} 次循環(huán)的進程id為:{}'.format(i, os.getpid()) # 線程也是基于進程實現(xiàn)的 if __name__ == '__main__': thread_poor = ThreadPoolExecutor(4) # 實例化一個線程池,設(shè)置線程數(shù)量為4 result = [] for i in range(20): result_thread = thread_poor.submit(work, (i,)) # 利用 submit 函數(shù)將任務(wù)添加至 work 函數(shù); # 需要注意的是這里不像進程池那樣使用賦值的形式傳入 work 函數(shù) result.append(result_thread) for res in result: print(res.result())
運行結(jié)果如下:
從運行結(jié)果可以看到,之前一同執(zhí)行的4個任務(wù)現(xiàn)在變成了一次只執(zhí)行一個任務(wù);每一個個線程都是在主進程 93215下執(zhí)行的,說明線程與進程還是有所區(qū)別的,雖然我們有多個線程任務(wù)在執(zhí)行,但是依然是在主進程下去完成的;同時我們還獲取到了 線程的返回值 第 {} 次循環(huán)的進程id為:{}'.format(i, os.getpid() 。
以上就是線程池的使用和常用方法,我們會發(fā)現(xiàn)線程池的使用實際上要比進程池的使用要容易一些。進程池我們需要考慮 join 與 close 等一些問題,但是線程池則不需要那么的嚴格,并且線程相對于進程要更加的輕量,使用起來也更加的便捷。
利用線程池實現(xiàn)抽獎小案例
案例代碼如下:
# coding:utf-8 import threading import random from concurrent.futures import ThreadPoolExecutor lock = threading.Lock() def luck_draw(arg): lock.acquire() # 從手機列表中隨機選出一個中獎手機,其他手機均未中獎 phone = random.choice(arg[0]) # 在從獎池中隨機選取一個獎品,視為該手機抽中的獎品 price = random.choice(arg[1]) prices.remove(price) phones.remove(phone) lock.release() return '恭喜手機尾號為{}的用戶,抽到{}'.format(str(phone)[-5:-1], price) if __name__ == '__main__': t = ThreadPoolExecutor(3) # 通過創(chuàng)建三個線程從而實現(xiàn)每個線程完成一項抽獎任務(wù) # 確定抽獎人數(shù) phone_num = int(input('請輸入抽獎的用戶人數(shù):')) # 模擬產(chǎn)生出相應(yīng)數(shù)量的手機號 phones = random.sample(range(13300000000, 19999999999), phone_num) # 這里設(shè)置的隨機號碼僅做演示效果 prices = ['一等獎:iPhone12 ProMax', '二等獎:ipad2021pro', '三等獎:air wetter'] result = [] for i in range(3): # 三個任務(wù),每個線程分配一個 t_result = t.submit(luck_draw, (phones, prices)) result.append(t_result) for res in result: print(res.result())
運行效果如下:
GIL全局鎖
本章節(jié)的開頭我們就說過,該部分沒有代碼的相關(guān)練習。僅僅是對 GIL全局鎖 做一個概念上的簡單啟蒙。
其他語言的線程與Python線程的區(qū)別
多線程與多進程的使用其實是比較復(fù)雜的,目前作為初學(xué)者來說涉及的還比較淺。最近的幾個章節(jié)介紹了 進程與線程在CPU的執(zhí)行方式,這里再進行拓展一下。
下面我們看一張圖:
依然是一個CPU 與4個核心(可以認為是4條跑道);
先看左邊的兩條跑道,是進程1創(chuàng)建的3個線程。這三條線程有一個去了 1core 跑道,另外兩條則去了 2core 跑道。線程之間有選擇性的進入了不同的跑道,當然進程1的主進程或者說是主線程可能會在 1core 跑道、也可能會在 2core 跑道,這是其他語言進行多線程的樣子。
再來看看右邊,Python 創(chuàng)建的進程2。當進程創(chuàng)建之后,包含主線程一共產(chǎn)生了3個線程,而這三個線程都跑到了 4core 跑道 上去。它不會像其他語言那樣去尋找不同的有空閑資源的跑道去執(zhí)行,而是僅僅在主進程所處的跑道去執(zhí)行線程。造成 Python 中的多線程無法在多條跑道執(zhí)行任務(wù)的主要原因就是因為 GIL全局鎖 ,這個 GIL 并不是 Python語法中添加上去的,而是 python解釋器 在執(zhí)行的時候自動加了這把 "鎖" 。
GIL 的作用
因為 GIL 鎖 的關(guān)系,使得 Python 的多線程無法在多個CPU跑道上去執(zhí)行任務(wù),它只能在單一CPU上進行工作。
這也限制了多線程的性能,畢竟 Python 的多線程只能在一條跑道上運行。跑道滿了,運行速度依然會慢。而在多個跑道上運行的任務(wù)必然是要比單一跑道效率會高很多。
Python創(chuàng)始人 Guido 之所以保留 GIL 鎖,其實也是為了線程之間的安全。雖然這個話題一直都在爭論,不過我們也有辦法去掉這個 GIL全局鎖。
默認的解釋器是 Python 自帶的解釋器,這里我們可以選擇一個叫做 pypy 的解釋器。通過它來執(zhí)行 Python 腳本是不含有 GIL全局鎖 的,但并不太推薦這種做法。
另外一種解決方法就是使用 多進程 + 多線程 的方式 來彌補這一短板上的問題,通過多進程在每個 CPU 跑道上執(zhí)行任務(wù),并且每個進程的跑道上再去執(zhí)行多個線程。 ,讓它們在各自的時間片上去運行。這些用法會在后續(xù)的章節(jié)會介紹到,在此只需要了解即可。
原文鏈接:https://blog.csdn.net/weixin_42250835/article/details/124127750
相關(guān)推薦
- 2022-09-24 Python?tkinter?列表框Listbox屬性詳情_python
- 2023-01-12 React?useCallback鉤子的作用方法demo_React
- 2022-04-04 前端小技巧:關(guān)閉瀏覽器時觸發(fā)事件
- 2022-12-09 Oracle遞歸查詢簡單示例_oracle
- 2022-01-18 報錯:Error occurred when invoke the listener‘s inter
- 2022-06-14 ASP.NET?Core?MVC中的布局(Layout)_基礎(chǔ)應(yīng)用
- 2022-07-29 Linux進程管理方法介紹_linux shell
- 2022-01-21 什么是前端開發(fā)?什么是后端開發(fā)?
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支