一、I/O 密集型任務(wù)處理
(一)理解 I/O 密集型任務(wù)
定義:I/O 密集型任務(wù)是指程序的大部分時(shí)間都用于等待輸入 / 輸出(I/O)操作完成,如文件讀取、*請(qǐng)求、數(shù)據(jù)庫(kù)查詢等。例如,從*上下載一個(gè)大型文件,在等待數(shù)據(jù)傳輸?shù)倪^(guò)程中,CPU 大部分時(shí)間是空閑的。
(二)多線程處理
原理:通過(guò)創(chuàng)建多個(gè)線程,當(dāng)一個(gè)線程在等待 I/O 操作時(shí),其他線程可以繼續(xù)執(zhí)行其他任務(wù),從而提高程序的整體效率。在 Python 中,可以使用threading
模塊來(lái)實(shí)現(xiàn)多線程。
示例代碼:
import threading import time def read_file(file_path): # 模擬讀取文件,這里使用了time.sleep來(lái)模擬I/O等待時(shí)間 print(f"開始讀取文件: {file_path}") time.sleep(3) print(f"文件讀取完成: {file_path}") file_paths = ["file1.txt", "file2.txt", "file3.txt"] threads = [] for file_path in file_paths: t = threading.Thread(target=read_file, args=(file_path,)) t.start() threads.append(t) for t in threads: t.join()
在上述代碼中,我們定義了一個(gè)read_file
函數(shù)來(lái)模擬讀取文件的操作。然后創(chuàng)建了多個(gè)線程來(lái)同時(shí)讀取不同的文件,t.start()
啟動(dòng)線程,t.join()
用于等待所有線程完成。
(三)多進(jìn)程處理
原理:多進(jìn)程與多線程類似,但進(jìn)程擁有自己獨(dú)立的內(nèi)存空間,對(duì)于一些需要更高隔離性和資源利用的場(chǎng)景更合適。在 Python 中,可以使用multiprocessing
模塊。
示例代碼:
import multiprocessing import time def read_file(file_path): print(f"開始讀取文件: {file_path}") time.sleep(3) print(f"文件讀取完成: {file_path}") if __name__ == "__main__": file_paths = ["file1.txt", "file2.txt", "file3.txt"] processes = [] for file_path in file_paths: p = multiprocessing.Process(target=read_file, args=(file_path,)) p.start() processes.append(p) for p in processes: p.join()
需要注意的是,在 Windows 系統(tǒng)下,使用multiprocessing模塊時(shí),if __name__ == "__main__"這一語(yǔ)句是必須的,以避免子進(jìn)程無(wú)限遞歸創(chuàng)建進(jìn)程。 (四)異步 I/O 處理 原理:異步 I/O 允許程序在等待 I/O 操作完成時(shí)不阻塞,可以繼續(xù)執(zhí)行其他任務(wù)。在 Python 中,asyncio庫(kù)是處理異步 I/O 的重要工具。 示例代碼:
import asyncio async def read_file_async(file_path): print(f"開始讀取文件: {file_path}") await asyncio.sleep(3) # 模擬異步I/O等待 print(f"文件讀取完成: {file_path}") async def main(): file_paths = ["file1.txt", "file2.txt", "file3.txt"] tasks = [] for file_path in file_paths: task = read_file_async(file_path) tasks.append(task) await asyncio.gather(*tasks) asyncio.run(main())
在這個(gè)示例中,read_file_async
函數(shù)是一個(gè)異步函數(shù),await asyncio.sleep(3)
模擬了異步 I/O 等待的過(guò)程。asyncio.gather
函數(shù)用于同時(shí)運(yùn)行多個(gè)異步任務(wù)。
二、組織異步代碼結(jié)構(gòu)
(一)分離關(guān)注點(diǎn)
含義:將不同的功能部分分離,例如,把 I/O 操作、數(shù)據(jù)處理、錯(cuò)誤處理等部分分開編寫。以*爬蟲為例,一個(gè)模塊負(fù)責(zé)發(fā)送 HTTP 請(qǐng)求(I/O 操作),另一個(gè)模塊負(fù)責(zé)解析 HTML 數(shù)據(jù)(數(shù)據(jù)處理),還有一個(gè)模塊負(fù)責(zé)記錄錯(cuò)誤。
優(yōu)點(diǎn):這樣的代碼結(jié)構(gòu)更清晰,便于維護(hù)和測(cè)試。如果 I/O 操作部分出現(xiàn)問(wèn)題,只需要關(guān)注和修改這部分代碼,而不會(huì)影響到其他部分。
(二)使用異步函數(shù)和協(xié)程
定義和使用:在 Python 的異步編程中,異步函數(shù)(用async def
定義)返回的是一個(gè)協(xié)程對(duì)象。協(xié)程是一種輕量級(jí)的線程,可以在異步 I/O 環(huán)境中高效地運(yùn)行。通過(guò)合理地定義和調(diào)用異步函數(shù),可以構(gòu)建出異步代碼的執(zhí)行流程。
示例:
import asyncio
async def getData():
# 模擬獲取數(shù)據(jù)的異步操作
await asyncio.sleep(2)
return "Data"
async def processData(data):
# 模擬數(shù)據(jù)處理的異步操作
await asyncio.sleep(1)
print(f"處理數(shù)據(jù): {data}")
async def main():
data = await getData()
await processData(data)
asyncio.run(main())