Python中六大超時(shí)處理方法介紹
Python 中常見(jiàn)的超時(shí)處理方法
在 Python 中,超時(shí)處理是一項(xiàng)非常常見(jiàn)的需求,尤其是在處理阻塞任務(wù)或并發(fā)任務(wù)時(shí)。以下是幾種常用的超時(shí)處理方法,每種方法適用于不同的場(chǎng)景。
1. signal 模塊
適用場(chǎng)景:當(dāng)需要對(duì)單個(gè)阻塞任務(wù)進(jìn)行超時(shí)控制時(shí),尤其是 CPU 密集型任務(wù)。通常用于限制一個(gè)長(zhǎng)期運(yùn)行的操作(例如復(fù)雜的計(jì)算任務(wù))。
優(yōu)點(diǎn):
- 無(wú)需額外安裝依賴(lài),直接使用 Python 標(biāo)準(zhǔn)庫(kù)。
- 簡(jiǎn)潔易用,適合處理單一阻塞任務(wù)的超時(shí)控制。
缺點(diǎn):
- 不支持多線(xiàn)程或多進(jìn)程,無(wú)法處理并發(fā)任務(wù)。
- Windows 平臺(tái)不支持 SIGALRM,因此此方法在 Windows 上無(wú)法使用。
適用場(chǎng)景:
適合在 UNIX-like 系統(tǒng)上對(duì)簡(jiǎn)單的阻塞任務(wù)進(jìn)行超時(shí)限制,如網(wǎng)絡(luò)請(qǐng)求、文件讀取等。
示例:
import signal def handler(signum, frame): """ 定義信號(hào)處理函數(shù) handler,當(dāng)接收到一個(gè)信號(hào)時(shí),這個(gè)函數(shù)會(huì)被調(diào)用。在函數(shù)內(nèi)部,拋出 TimeoutError 異常,表示操作超時(shí)。 signum:表示接收到的信號(hào)編號(hào)(在本例中是 SIGALRM 信號(hào))。 frame:是當(dāng)前的堆棧幀信息,我們?cè)谶@個(gè)示例中并不使用它。 當(dāng) handler 被調(diào)用時(shí),它會(huì)拋出一個(gè) TimeoutError 異常,表示任務(wù)超時(shí)。 """ raise TimeoutError("操作超時(shí)") signal.signal(signal.SIGALRM, handler) # 通過(guò) signal.signal() 函數(shù)將 SIGALRM 信號(hào)與 handler 關(guān)聯(lián)。即:當(dāng)程序接收到 SIGALRM 信號(hào)時(shí),會(huì)執(zhí)行 handler 函數(shù)。 # SIGALRM 是一個(gè)定時(shí)信號(hào),通常用于指定某個(gè)操作的超時(shí)時(shí)間。在這個(gè)例子中,下面我會(huì)設(shè)置一個(gè)定時(shí)器,讓它在 5 秒后發(fā)送 SIGALRM 信號(hào)。 signal.alarm(5) # 設(shè)置一個(gè)定時(shí)器,定時(shí)器將在 5 秒后向當(dāng)前進(jìn)程發(fā)出一個(gè) SIGALRM 信號(hào)。 try: while True: # 模擬長(zhǎng)時(shí)間運(yùn)行的任務(wù) pass except TimeoutError: print("操作超時(shí)") finally: signal.alarm(0) # 取消定時(shí)器,防止程序結(jié)束后定時(shí)器仍然生效。
設(shè)置信號(hào)處理器:signal.signal(signal.SIGALRM, handler) 讓程序在接收到 SIGALRM 信號(hào)時(shí)執(zhí)行 handler 函數(shù)。
啟動(dòng)定時(shí)器:signal.alarm(5) 啟動(dòng)一個(gè)定時(shí)器,在 5 秒后發(fā)送 SIGALRM 信號(hào)。
模擬長(zhǎng)時(shí)間任務(wù):while True: pass 模擬一個(gè)無(wú)限循環(huán),表示一個(gè)長(zhǎng)時(shí)間運(yùn)行的任務(wù)。
觸發(fā)超時(shí):在 5 秒后,SIGALRM 信號(hào)會(huì)被發(fā)送,handler 函數(shù)會(huì)被調(diào)用,拋出 TimeoutError 異常。
捕獲超時(shí)異常:except TimeoutError: 捕獲超時(shí)異常,并打印 “操作超時(shí)”。
取消定時(shí)器:finally: signal.alarm(0) 取消定時(shí)器,避免定時(shí)器在任務(wù)完成后繼續(xù)觸發(fā)。
注意:在 Windows 上會(huì)報(bào)錯(cuò) AttributeError: module 'signal' has no attribute 'SIGALRM'。此方法僅適用于 UNIX-like 系統(tǒng)。
2. threading 模塊
適用場(chǎng)景:適合并發(fā)執(zhí)行獨(dú)立任務(wù)的場(chǎng)景,特別是當(dāng)每個(gè)任務(wù)的執(zhí)行時(shí)間需要控制時(shí)。適用于多線(xiàn)程并發(fā)處理任務(wù)。
優(yōu)點(diǎn):
- 支持多線(xiàn)程,適合獨(dú)立的并發(fā)任務(wù)。
- 靈活的超時(shí)處理,可以通過(guò) Timer 定時(shí)器設(shè)置任務(wù)超時(shí)。
缺點(diǎn):
- 線(xiàn)程管理較為復(fù)雜,涉及顯式創(chuàng)建和控制線(xiàn)程。
- 相比 asyncio 和其他異步框架,線(xiàn)程開(kāi)銷(xiāo)較大,可能導(dǎo)致資源競(jìng)爭(zhēng)和上下文切換。
適用場(chǎng)景:
適合任務(wù)之間相對(duì)獨(dú)立,且需要并發(fā)執(zhí)行的場(chǎng)景,如并行任務(wù)、爬蟲(chóng)等。
示例:
通過(guò)創(chuàng)建兩個(gè)線(xiàn)程來(lái)同時(shí)執(zhí)行一個(gè)長(zhǎng)時(shí)間運(yùn)行的任務(wù)和超時(shí)處理功能~
import threading import time def task(): time.sleep(10) # 模擬長(zhǎng)時(shí)間任務(wù) return "任務(wù)完成" def timeout_fun(): print("任務(wù)超時(shí)") timeout_time = 5 t1 = threading.Thread(target=task) t2 = threading.Timer(timeout_time, timeout_fun) # 創(chuàng)建一個(gè)定時(shí)器 t2,它會(huì)在 5 秒后執(zhí)行 timeout_fun() 函數(shù)。如果任務(wù)在 5 秒內(nèi)未完成,timeout_fun 就會(huì)被調(diào)用,打印超時(shí)信息。 t1.start() t2.start() t1.join() # 會(huì)讓主線(xiàn)程等待 t1 線(xiàn)程完成執(zhí)行。如果 t1 在 10 秒內(nèi)完成,主線(xiàn)程才會(huì)繼續(xù)執(zhí)行下一步。如果 t1 超過(guò)了 10 秒,主線(xiàn)程會(huì)繼續(xù)等待。 t2.cancel() # 取消定時(shí)器 t2,如果 t1 任務(wù)在 5 秒內(nèi)完成,定時(shí)器 t2 會(huì)被取消,防止超時(shí)處理函數(shù) timeout_fun() 被觸發(fā)。
任務(wù)線(xiàn)程啟動(dòng):t1 開(kāi)始執(zhí)行 task(),模擬長(zhǎng)時(shí)間運(yùn)行的任務(wù)。
定時(shí)器啟動(dòng):t2 啟動(dòng)并在 5 秒后觸發(fā)超時(shí)處理。
任務(wù)超時(shí)處理:如果任務(wù) t1 在 5 秒內(nèi)沒(méi)有完成,定時(shí)器 t2 會(huì)觸發(fā)并執(zhí)行 timeout_fun(),打印 “任務(wù)超時(shí)”。
任務(wù)完成:如果任務(wù)在 5 秒內(nèi)完成,t2.cancel() 會(huì)被調(diào)用,取消超時(shí)處理。
3. concurrent.futures 模塊
適用場(chǎng)景:適合處理多個(gè)并發(fā)任務(wù)的超時(shí)控制,尤其是涉及 I/O 密集型任務(wù)時(shí)。該模塊簡(jiǎn)化了線(xiàn)程池和進(jìn)程池的管理,適合有多個(gè)任務(wù)并發(fā)執(zhí)行的情況。
優(yōu)點(diǎn):
- 提供了線(xiàn)程池和進(jìn)程池的高層次接口,簡(jiǎn)化并發(fā)任務(wù)的管理。
- 內(nèi)置超時(shí)控制,可以直接指定任務(wù)的超時(shí)時(shí)間。
- 支持并發(fā)執(zhí)行,易于實(shí)現(xiàn)任務(wù)的并發(fā)和超時(shí)控制。
缺點(diǎn):
- 進(jìn)程池和線(xiàn)程池的性能在不同場(chǎng)景下有所差異,需要根據(jù)任務(wù)特點(diǎn)選擇合適的池類(lèi)型。
- 如果任務(wù)較少或簡(jiǎn)單,使用 concurrent.futures 可能顯得稍復(fù)雜。
適用場(chǎng)景:
當(dāng)有多個(gè)并發(fā)任務(wù)需要執(zhí)行,并且每個(gè)任務(wù)的超時(shí)時(shí)間需要控制時(shí),可以使用此模塊。
示例:
import concurrent.futures import time def task(): time.sleep(10) # 模擬長(zhǎng)時(shí)間任務(wù) return "任務(wù)完成" with concurrent.futures.ThreadPoolExecutor() as executor: future = executor.submit(task) # submit() 方法異步提交任務(wù)并返回一個(gè) Future 對(duì)象,F(xiàn)uture 對(duì)象提供了管理任務(wù)的接口,比如獲取任務(wù)結(jié)果或設(shè)置超時(shí)等。 try: result = future.result(timeout=5) # 用來(lái)獲取 task() 函數(shù)的執(zhí)行結(jié)果。timeout=5 表示最多等待 5 秒鐘,如果在 5 秒內(nèi)任務(wù)沒(méi)有完成,就會(huì)拋出 TimeoutError 異常。 # 如果 task() 在 5 秒內(nèi)完成,future.result() 會(huì)返回任務(wù)的返回值(即 "任務(wù)完成"),然后打印出結(jié)果。 print(result) except concurrent.futures.TimeoutError: print("任務(wù)超時(shí)")
4. asyncio 模塊
適用場(chǎng)景:適合 I/O 密集型任務(wù),特別是需要并發(fā)處理的異步任務(wù)。適用于需要高并發(fā)的場(chǎng)景,如異步網(wǎng)絡(luò)請(qǐng)求、文件 I/O、數(shù)據(jù)庫(kù)操作等。
優(yōu)點(diǎn):
- 非常高效,尤其適合處理 I/O 密集型任務(wù),能夠?qū)崿F(xiàn)大量任務(wù)的并發(fā)。
- 異步編程模式(async/await)讓代碼結(jié)構(gòu)清晰且易于管理。
缺點(diǎn):
- 需要理解和掌握異步編程,可能對(duì)不熟悉異步模型的開(kāi)發(fā)者有一定門(mén)檻。
- 對(duì) CPU 密集型任務(wù)不適用,因?yàn)?asyncio 并不通過(guò)多核來(lái)并行處理任務(wù)。
適用場(chǎng)景:
當(dāng)任務(wù)是 I/O 密集型且需要高并發(fā)時(shí)(如處理大量并發(fā)請(qǐng)求或文件操作),asyncio 是理想的選擇。
示例:
import asyncio async def task(): await asyncio.sleep(10) # 模擬一個(gè)長(zhǎng)時(shí)間運(yùn)行的異步任務(wù),asyncio.sleep(10) 會(huì)讓當(dāng)前任務(wù)掛起 10 秒,在這段時(shí)間內(nèi)不會(huì)占用 CPU 資源。類(lèi)似于 time.sleep(),但它是非阻塞的,因此可以在等待時(shí)繼續(xù)執(zhí)行其他任務(wù)。 return "任務(wù)完成" async def main(): try: result = await asyncio.wait_for(task(), timeout=5) # asyncio.wait_for() 用來(lái)執(zhí)行 task() 并設(shè)置超時(shí)。如果 task() 在指定的時(shí)間內(nèi)(這里是 5 秒)完成,wait_for 會(huì)返回 task() 的結(jié)果(即 "任務(wù)完成")。 print(result) except asyncio.TimeoutError: print("任務(wù)超時(shí)") asyncio.run(main()) # 用來(lái)啟動(dòng)事件循環(huán)并執(zhí)行 main() 協(xié)程。asyncio.run() 會(huì)創(chuàng)建一個(gè)新的事件循環(huán),運(yùn)行傳入的協(xié)程,直到協(xié)程執(zhí)行完畢并返回結(jié)果。這個(gè)方法會(huì)自動(dòng)管理事件循環(huán)的創(chuàng)建和關(guān)閉。
5. eventlet
適用場(chǎng)景:適用于需要處理高并發(fā)的 I/O 密集型任務(wù),特別是在需要大量并發(fā)請(qǐng)求時(shí)。適合用來(lái)構(gòu)建高并發(fā)的 Web 服務(wù)器或網(wǎng)絡(luò)爬蟲(chóng)。
優(yōu)點(diǎn):
- 高效的綠色線(xiàn)程模型,能夠以較低的資源消耗處理大量并發(fā)任務(wù)。
- 特別適用于 I/O 密集型任務(wù),減少線(xiàn)程和進(jìn)程的開(kāi)銷(xiāo)。
缺點(diǎn):
- 需要額外安裝第三方庫(kù) eventlet。
- 對(duì) CPU 密集型任務(wù)性能較差,因?yàn)槠渫ㄟ^(guò)協(xié)程模擬并發(fā),無(wú)法利用多核 CPU。
適用場(chǎng)景:
高并發(fā) I/O 密集型任務(wù),尤其是需要在同一線(xiàn)程中處理大量并發(fā)請(qǐng)求的場(chǎng)景。
示例:
import eventlet def long_task(): eventlet.sleep(10) # 通過(guò) eventlet.sleep(10) 模擬一個(gè)阻塞的操作,實(shí)際上是讓出當(dāng)前綠色線(xiàn)程 10 秒鐘。 # 注意:eventlet.sleep() 并不是真的讓程序進(jìn)入休眠,它讓出控制權(quán),使得其他綠色線(xiàn)程可以執(zhí)行。這是 eventlet 模擬并發(fā)的方式。 return "任務(wù)完成" def task_with_timeout(): pool = eventlet.GreenPool() # 創(chuàng)建綠色線(xiàn)程池 result = pool.spawn(long_task) # spawn() 方法會(huì)異步地執(zhí)行 long_task,并返回一個(gè) GreenThread 對(duì)象。這個(gè)對(duì)象表示正在運(yùn)行的綠色線(xiàn)程。 try: # wait() 方法會(huì)阻塞當(dāng)前線(xiàn)程,直到綠色線(xiàn)程執(zhí)行完畢并返回結(jié)果。它的 timeout 參數(shù)用于設(shè)置最大等待時(shí)間,這里設(shè)置為 5 秒。如果綠色線(xiàn)程在 5 秒內(nèi)沒(méi)有完成,wait() 方法會(huì)拋出 eventlet.timeout.Timeout 異常。 return result.wait(timeout=5) # 設(shè)置超時(shí)時(shí)間為5秒 except eventlet.timeout.Timeout: return "任務(wù)超時(shí)" print(task_with_timeout())
6. func-timeout
適用場(chǎng)景:當(dāng)你需要限制某個(gè)單獨(dú)函數(shù)的執(zhí)行時(shí)間,尤其是任務(wù)本身可能是阻塞型的。
優(yōu)點(diǎn):
- 極簡(jiǎn)的接口,專(zhuān)門(mén)用于控制函數(shù)的超時(shí),使用方便。
- 不需要處理多線(xiàn)程或并發(fā)任務(wù),簡(jiǎn)單直觀。
缺點(diǎn):
- 僅適用于單個(gè)函數(shù),無(wú)法處理多個(gè)任務(wù)或并發(fā)任務(wù)的超時(shí)。
- 不支持多線(xiàn)程或進(jìn)程池的管理。
適用場(chǎng)景:
當(dāng)你只需要對(duì)某個(gè)特定函數(shù)進(jìn)行超時(shí)控制時(shí),func-timeout 非常方便。
示例:
from func_timeout import func_timeout, FunctionTimedOut def long_task(): import time time.sleep(10) # 模擬長(zhǎng)時(shí)間任務(wù) return "任務(wù)完成" try: result = func_timeout(5, long_task) # 設(shè)置超時(shí)時(shí)間為5秒 print(result) except FunctionTimedOut: print("任務(wù)超時(shí)")
總結(jié)對(duì)比:
方法 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|---|
signal | 單一阻塞任務(wù)的超時(shí) | 簡(jiǎn)單直接,適用于 UNIX-like 系統(tǒng) | 不支持多線(xiàn)程,無(wú)法處理并發(fā)任務(wù),僅適用于 UNIX-like 系統(tǒng) |
threading | 多線(xiàn)程任務(wù)并發(fā)執(zhí)行 | 靈活支持多線(xiàn)程,適合獨(dú)立任務(wù) | 線(xiàn)程管理復(fù)雜,開(kāi)銷(xiāo)較大,可能出現(xiàn)資源競(jìng)爭(zhēng) |
concurrent.futures | 多并發(fā)任務(wù)并行執(zhí)行 | 高層次接口,線(xiàn)程池/進(jìn)程池管理簡(jiǎn)潔,支持超時(shí)控制 | 對(duì)任務(wù)較少時(shí)可能過(guò)于復(fù)雜,線(xiàn)程和進(jìn)程開(kāi)銷(xiāo)較大 |
asyncio | 異步 I/O 密集型任務(wù) | 高效處理 I/O 密集型任務(wù),適合大量并發(fā) | 異步編程模型有學(xué)習(xí)曲線(xiàn),不適合 CPU 密集型任務(wù) |
Eventlet | 高并發(fā) I/O 密集型任務(wù) | 高效的綠色線(xiàn)程管理,低資源消耗 | 不支持 CPU 密集型任務(wù),額外安裝依賴(lài) |
func-timeout | 單獨(dú)函數(shù)的超時(shí)控制 | 極簡(jiǎn)接口,快速實(shí)現(xiàn)超時(shí)控制 | 僅適用于單個(gè)函數(shù),無(wú)法處理并發(fā)任務(wù) |
到此這篇關(guān)于Python中六大超時(shí)處理方法介紹的文章就介紹到這了,更多相關(guān)Python超時(shí)處理方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python使用arp欺騙偽造網(wǎng)關(guān)的方法
這篇文章主要介紹了python使用arp欺騙偽造網(wǎng)關(guān)的方法,涉及Python偽造網(wǎng)關(guān)的相關(guān)技巧,需要的朋友可以參考下2015-04-04Flask與數(shù)據(jù)庫(kù)的交互插件Flask-Sqlalchemy的使用
在構(gòu)建Web應(yīng)用時(shí),與數(shù)據(jù)庫(kù)的交互是必不可少的部分,本文主要介紹了Flask與數(shù)據(jù)庫(kù)的交互插件Flask-Sqlalchemy的使用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03淺談spring boot 集成 log4j 解決與logback沖突的問(wèn)題
今天小編就為大家分享一篇淺談spring boot 集成 log4j 解決與logback沖突的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02Python 測(cè)試框架unittest和pytest的優(yōu)劣
這篇文章主要介紹了Python 測(cè)試框架unittest和pytest的優(yōu)劣,幫助大家更好的進(jìn)行python程序的測(cè)試,感興趣的朋友可以了解下2020-09-09