一、理解多線程的適用場(chǎng)景 I/O 密集型任務(wù) 當(dāng)程序主要是在等待外部輸入 / 輸出(I/O)操作完成時(shí),如讀取文件、*請(qǐng)求等,多線程可以顯著提高效率。例如,在一個(gè)*爬蟲程序中,需要從多個(gè)網(wǎng)頁(yè)獲取數(shù)據(jù)。如果使用單線程,程序會(huì)依次請(qǐng)求每個(gè)網(wǎng)頁(yè),等待一個(gè)網(wǎng)頁(yè)的數(shù)據(jù)完全返回后再請(qǐng)求下一個(gè)。但如果使用多線程,可以同時(shí)發(fā)起多個(gè)網(wǎng)頁(yè)請(qǐng)求,當(dāng)一個(gè)線程在等待某個(gè)網(wǎng)頁(yè)的響應(yīng)時(shí),其他線程可以繼續(xù)處理其他網(wǎng)頁(yè)的請(qǐng)求。 示例代碼: 收起
import requests import threading def download_page(url): resp*e = requests.get(url) print(f"Downloaded {url}, status code: {resp*e.status_code}") urls = ["https://www.example1.com", "https://www.example2.com", "https://www.example3.com"] threads = [] for url in urls: thread = threading.Thread(target=download_page, args=(url,)) thread.start() threads.append(thread) for thread in threads: thread.join()
在這個(gè)例子中,我們創(chuàng)建了多個(gè)線程來(lái)并發(fā)地下載網(wǎng)頁(yè)內(nèi)容。每個(gè)線程負(fù)責(zé)下載一個(gè)指定的網(wǎng)頁(yè),這樣可以大大加快整個(gè)下載過(guò)程。 不適合 CPU 密集型任務(wù)(在 CPython 解釋器下) 由于 Python 的全局解釋器鎖(GIL)的存在,在 CPython(最常用的 Python 解釋器)中,多線程在處理 CPU 密集型任務(wù)(如復(fù)雜的數(shù)學(xué)計(jì)算)時(shí),并不能充分利用多核 CPU 的優(yōu)勢(shì)。因?yàn)橥粫r(shí)間只有一個(gè)線程可以執(zhí)行 Python 字節(jié)碼。例如,一個(gè)計(jì)算斐波那契數(shù)列的函數(shù),在多線程環(huán)境下運(yùn)行多個(gè)這樣的計(jì)算任務(wù),并不會(huì)比單線程快很多,因?yàn)槎鄠€(gè)線程會(huì)競(jìng)爭(zhēng) GIL,大部分時(shí)間都在等待獲取 GIL 來(lái)執(zhí)行代碼。 不過(guò),如果是在一些不帶有 GIL 的 Python 解釋器(如 Jython 或 IronPython)下,多線程對(duì)于 CPU 密集型任務(wù)可以更好地利用多核 CPU。
二、線程的創(chuàng)建和啟動(dòng) 使用threading模塊創(chuàng)建線程 可以通過(guò)定義一個(gè)函數(shù)作為線程要執(zhí)行的任務(wù),然后使用threading.Thread類來(lái)創(chuàng)建線程對(duì)象。線程對(duì)象的target參數(shù)指定要執(zhí)行的函數(shù),args參數(shù)(可選)指定傳遞給函數(shù)的參數(shù)。 示例:
import threading
def print_numbers():
for i in range(10):
print(i)
thread = threading.Thread(target=print_numbers)
thread.start()