Python使用thread模塊實(shí)現(xiàn)多線程的操作
介紹:
線程(Threads)是操作系統(tǒng)提供的一種輕量級(jí)的執(zhí)行單元,可以在一個(gè)進(jìn)程內(nèi)并發(fā)執(zhí)行多個(gè)任務(wù)。每個(gè)線程都有自己的執(zhí)行上下文,包括棧、寄存器和程序計(jì)數(shù)器。
在Python中,可以使用threading模塊創(chuàng)建和管理線程。線程可以同時(shí)執(zhí)行多個(gè)任務(wù),可以在一個(gè)線程中執(zhí)行耗時(shí)操作,而不會(huì)阻塞其他線程的執(zhí)行。線程之間共享進(jìn)程的資源,如內(nèi)存空間,因此需要注意線程安全的問(wèn)題。
然而,Python的線程在特定情況下可能會(huì)受到全局解釋器鎖(Global Interpreter Lock,GIL)的限制。GIL是一種機(jī)制,它確保同一時(shí)刻只有一個(gè)線程可以執(zhí)行Python字節(jié)碼。這意味著在多線程場(chǎng)景下,即使有多個(gè)線程,也無(wú)法真正實(shí)現(xiàn)并行執(zhí)行。因此,在CPU密集型的任務(wù)中,Python的線程并不能充分利用多核處理器的能力。
1、導(dǎo)入threading模塊
在使用Python線程之前,首先需要導(dǎo)入 threading 模塊??梢允褂靡韵抡Z(yǔ)句導(dǎo)入該模塊:
import threading
2、創(chuàng)建線程
使用 threading.Thread 類創(chuàng)建線程對(duì)象。可以通過(guò)傳遞一個(gè)可調(diào)用的目標(biāo)函數(shù)和其他參數(shù)來(lái)實(shí)例化線程對(duì)象。目標(biāo)函數(shù)是線程實(shí)際執(zhí)行的任務(wù)。
# 定義一個(gè)目標(biāo)函數(shù)作為線程的執(zhí)行任務(wù)
def my_task(arg1, arg2):
# 執(zhí)行任務(wù)的代碼
# 創(chuàng)建線程對(duì)象
my_thread = threading.Thread(target=my_task, args=(arg1, arg2))3、啟動(dòng)線程
通過(guò)調(diào)用線程對(duì)象的 start() 方法來(lái)啟動(dòng)線程。啟動(dòng)線程后,它將在后臺(tái)運(yùn)行,執(zhí)行目標(biāo)函數(shù)中的代碼。
my_thread.start()
4、等待線程完成
可以使用 join() 方法等待線程執(zhí)行完畢。調(diào)用 join() 方法會(huì)阻塞當(dāng)前線程,直到目標(biāo)線程執(zhí)行完成。
my_thread.join()
5、線程同步
在多線程編程中,線程之間的同步是一項(xiàng)重要的任務(wù),旨在確保線程按照預(yù)期的順序執(zhí)行,并避免競(jìng)態(tài)條件和數(shù)據(jù)不一致的問(wèn)題。Python提供了幾種同步原語(yǔ),常用的包括鎖(Lock)、信號(hào)量(Semaphore)、事件(Event)和條件變量(Condition)。下面詳細(xì)介紹這些同步原語(yǔ)的特點(diǎn)和使用方法:
鎖(Lock)
鎖是一種最基本的同步原語(yǔ),在Python中由 threading.Lock 類實(shí)現(xiàn)。它提供了兩個(gè)主要方法:acquire() 和 release()。一個(gè)線程可以通過(guò)調(diào)用 acquire() 來(lái)獲取鎖,如果鎖當(dāng)前沒有被其他線程持有,則該線程將獲得鎖并繼續(xù)執(zhí)行,否則將被阻塞直到鎖被釋放。當(dāng)線程完成對(duì)臨界區(qū)的訪問(wèn)后,應(yīng)該調(diào)用 release() 來(lái)釋放鎖,以便其他線程可以獲取它。
import threading
# 創(chuàng)建鎖對(duì)象
lock = threading.Lock()
# 線程函數(shù)
def thread_function():
lock.acquire()
# 臨界區(qū)代碼
lock.release()鎖還支持上下文管理器的使用方式,可以使用 with 語(yǔ)句來(lái)自動(dòng)獲取和釋放鎖:
import threading
# 創(chuàng)建鎖對(duì)象
lock = threading.Lock()
# 線程函數(shù)
def thread_function():
with lock:
# 臨界區(qū)代碼信號(hào)量(Semaphore)
信號(hào)量是一種更高級(jí)的同步原語(yǔ),用于控制對(duì)共享資源的并發(fā)訪問(wèn)。Python中的信號(hào)量由 threading.Semaphore 類實(shí)現(xiàn)。信號(hào)量維護(hù)一個(gè)內(nèi)部計(jì)數(shù)器,線程可以通過(guò)調(diào)用 acquire() 來(lái)減少計(jì)數(shù)器的值,如果計(jì)數(shù)器為零,則線程將被阻塞。線程在完成對(duì)共享資源的訪問(wèn)后,應(yīng)該調(diào)用 release() 來(lái)增加計(jì)數(shù)器的值,以便其他線程可以獲取信號(hào)量。
import threading
# 創(chuàng)建信號(hào)量對(duì)象
semaphore = threading.Semaphore(value=3) # 設(shè)置初始計(jì)數(shù)器值為3
# 線程函數(shù)
def thread_function():
semaphore.acquire()
# 訪問(wèn)共享資源
semaphore.release()信號(hào)量的計(jì)數(shù)器可以控制同時(shí)訪問(wèn)共享資源的線程數(shù)量。
事件(Event)
事件是一種用于線程間通信的同步原語(yǔ),由 threading.Event 類實(shí)現(xiàn)。事件有兩種狀態(tài):已設(shè)置和未設(shè)置。線程可以通過(guò)調(diào)用 set() 來(lái)設(shè)置事件,將其狀態(tài)設(shè)置為已設(shè)置;通過(guò)調(diào)用 clear() 可以將事件狀態(tài)設(shè)置為未設(shè)置。線程可以通過(guò)調(diào)用 wait() 來(lái)等待事件的設(shè)置,如果事件已設(shè)置,則線程可以繼續(xù)執(zhí)行,否則將被阻塞。
import threading
# 創(chuàng)建事件對(duì)象
event = threading.Event()
# 線程函數(shù)
def thread_function():
event.wait() # 等待事件設(shè)置
# 執(zhí)行操作
# 主線程設(shè)置事件
event.set()事件還可以使用 is_set() 方法來(lái)檢查事件的狀態(tài)。
條件變量(Condition)
條件變量是一種復(fù)雜的同步原語(yǔ),由 threading.Condition 類實(shí)現(xiàn)。它提供了一個(gè)條件隊(duì)列,允許線程等待某個(gè)條件的發(fā)生。條件變量結(jié)合鎖一起使用,可以實(shí)現(xiàn)更復(fù)雜的線程間同步。
import threading
# 創(chuàng)建條件變量對(duì)象
condition = threading.Condition()
# 線程函數(shù) A
def thread_function_a():
with condition:
while not condition_predicate():
condition.wait()
# 執(zhí)行操作
# 線程函數(shù) B
def thread_function_b():
with condition:
# 修改條件
condition.notify() # 通知等待的線程
# 主線程
with condition:
# 修改條件
condition.notify() # 通知等待的線程在線程函數(shù) A 中,線程會(huì)等待條件謂詞成立的情況下才繼續(xù)執(zhí)行,否則會(huì)調(diào)用 wait() 方法將線程掛起。線程函數(shù) B 可以在某個(gè)條件發(fā)生變化時(shí)調(diào)用 notify() 方法來(lái)通知等待的線程。
6、共享數(shù)據(jù)
共享數(shù)據(jù)是指多個(gè)線程同時(shí)訪問(wèn)和修改的數(shù)據(jù)。當(dāng)多個(gè)線程同時(shí)讀寫共享數(shù)據(jù)時(shí),可能會(huì)發(fā)生競(jìng)態(tài)條件(Race Condition)和數(shù)據(jù)損壞的問(wèn)題。為了確保線程安全性,需要采取適當(dāng)?shù)耐酱胧﹣?lái)保護(hù)共享數(shù)據(jù)。以下是一些常用的同步機(jī)制和技術(shù):
鎖(Lock)
鎖是一種最常見的同步原語(yǔ),用于保護(hù)共享數(shù)據(jù)的互斥訪問(wèn)。在多線程環(huán)境中,一個(gè)線程可以通過(guò)獲取鎖來(lái)獨(dú)占地訪問(wèn)共享數(shù)據(jù),其他線程必須等待鎖釋放后才能訪問(wèn)。鎖可以使用 threading.Lock 類來(lái)實(shí)現(xiàn),通過(guò)調(diào)用 acquire() 和 release() 方法來(lái)獲取和釋放鎖。
import threading
# 創(chuàng)建鎖對(duì)象
lock = threading.Lock()
# 共享數(shù)據(jù)
shared_data = 0
# 線程函數(shù)
def thread_function():
global shared_data
lock.acquire()
# 訪問(wèn)和修改共享數(shù)據(jù)
shared_data += 1
lock.release()在訪問(wèn)共享數(shù)據(jù)之前獲取鎖,確保同一時(shí)間只有一個(gè)線程可以修改數(shù)據(jù),從而避免競(jìng)態(tài)條件。
信號(hào)量(Semaphore)
信號(hào)量也可以用于保護(hù)共享數(shù)據(jù)的訪問(wèn),在多線程環(huán)境中控制并發(fā)訪問(wèn)的數(shù)量。信號(hào)量維護(hù)一個(gè)內(nèi)部計(jì)數(shù)器,線程在訪問(wèn)共享數(shù)據(jù)之前通過(guò)獲取信號(hào)量來(lái)減少計(jì)數(shù)器的值,如果計(jì)數(shù)器為零,則線程將被阻塞。線程在完成對(duì)共享數(shù)據(jù)的訪問(wèn)后,通過(guò)釋放信號(hào)量來(lái)增加計(jì)數(shù)器的值,從而允許其他線程繼續(xù)訪問(wèn)。
import threading
# 創(chuàng)建信號(hào)量對(duì)象
semaphore = threading.Semaphore()
# 共享數(shù)據(jù)
shared_data = 0
# 線程函數(shù)
def thread_function():
global shared_data
semaphore.acquire()
# 訪問(wèn)和修改共享數(shù)據(jù)
shared_data += 1
semaphore.release()通過(guò)適當(dāng)設(shè)置信號(hào)量的初始值,可以控制同時(shí)訪問(wèn)共享數(shù)據(jù)的線程數(shù)量。
其他同步原語(yǔ)
Python還提供了其他一些同步原語(yǔ),如條件變量(Condition)和事件(Event)。它們可以用于更復(fù)雜的同步需求,如線程之間的通信和等待特定條件的發(fā)生。
import threading
# 創(chuàng)建條件變量對(duì)象
condition = threading.Condition()
# 共享數(shù)據(jù)
shared_data = []
# 線程函數(shù) A
def thread_function_a():
with condition:
while not condition_predicate():
condition.wait()
# 訪問(wèn)和修改共享數(shù)據(jù)
# 線程函數(shù) B
def thread_function_b():
with condition:
# 修改條件
condition.notify() # 通知等待的線程在上述示例中,線程函數(shù) A等待條件謂詞成立的情況下才能訪問(wèn)共享數(shù)據(jù),線程函數(shù) B在條件發(fā)生變化時(shí)通過(guò) notify() 方法通知等待的線程。
7、線程狀態(tài)
線程狀態(tài)是指線程在不同的時(shí)間點(diǎn)上所處的狀態(tài),它反映了線程的執(zhí)行情況和可用性。在多線程編程中,線程可以處于以下幾種不同的狀態(tài):
新建(New)狀態(tài)
當(dāng)創(chuàng)建線程對(duì)象但尚未啟動(dòng)線程時(shí),線程處于新建狀態(tài)。此時(shí)線程對(duì)象已經(jīng)被創(chuàng)建,但尚未分配系統(tǒng)資源和執(zhí)行代碼??梢酝ㄟ^(guò)實(shí)例化線程類或者從線程池中獲取線程來(lái)創(chuàng)建新線程。
import threading # 創(chuàng)建新線程對(duì)象 thread = threading.Thread(target=thread_function)
就緒(Runnable)狀態(tài)
當(dāng)線程準(zhǔn)備好執(zhí)行,但由于系統(tǒng)調(diào)度的原因還未開始執(zhí)行時(shí),線程處于就緒狀態(tài)。線程已經(jīng)分配了系統(tǒng)資源,并且等待調(diào)度器將其放入運(yùn)行隊(duì)列中。多個(gè)就緒狀態(tài)的線程可能會(huì)競(jìng)爭(zhēng)CPU資源,調(diào)度器會(huì)根據(jù)調(diào)度算法決定哪個(gè)線程被選中執(zhí)行。
運(yùn)行(Running)狀態(tài)
當(dāng)線程獲得CPU資源并開始執(zhí)行線程函數(shù)時(shí),線程處于運(yùn)行狀態(tài)。此時(shí)線程的代碼正在被執(zhí)行,它可能會(huì)與其他線程并發(fā)執(zhí)行或通過(guò)時(shí)間片輪轉(zhuǎn)進(jìn)行切換。只有一個(gè)線程可以處于運(yùn)行狀態(tài)。
阻塞(Blocked)狀態(tài)
當(dāng)線程被暫停執(zhí)行,等待某個(gè)條件的發(fā)生時(shí),線程處于阻塞狀態(tài)。在阻塞狀態(tài)下,線程不會(huì)占用CPU資源,直到滿足特定條件后才能繼續(xù)執(zhí)行。常見的阻塞原因包括等待I/O操作、獲取鎖失敗、等待其他線程的通知等。
終止(Terminated)狀態(tài)
當(dāng)線程完成了它的執(zhí)行任務(wù)或被顯式終止時(shí),線程處于終止?fàn)顟B(tài)。線程函數(shù)執(zhí)行完畢或者出現(xiàn)異常時(shí),線程將自動(dòng)終止。也可以通過(guò)調(diào)用線程對(duì)象的 join() 方法來(lái)等待線程執(zhí)行完畢。
# 等待線程執(zhí)行完畢 thread.join()
線程狀態(tài)之間可以相互轉(zhuǎn)換,線程的狀態(tài)轉(zhuǎn)換通常由操作系統(tǒng)的調(diào)度器和線程的執(zhí)行情況決定。例如,當(dāng)線程處于就緒狀態(tài)并獲得CPU資源時(shí),它將進(jìn)入運(yùn)行狀態(tài);當(dāng)線程在執(zhí)行期間發(fā)生阻塞,它將進(jìn)入阻塞狀態(tài);當(dāng)線程執(zhí)行完畢或被終止時(shí),它將進(jìn)入終止?fàn)顟B(tài)。
8、線程屬性和方法
threading.Thread 類提供了一些屬性和方法來(lái)管理和操作線程。其中一些常用的屬性和方法包括:
name:獲取或設(shè)置線程的名稱。ident:獲取線程的標(biāo)識(shí)符。is_alive():檢查線程是否處于活動(dòng)狀態(tài)。setDaemon(daemonic):將線程設(shè)置為守護(hù)線程,當(dāng)主線程退出時(shí),守護(hù)線程也會(huì)被終止。start():?jiǎn)?dòng)線程。join(timeout):等待線程執(zhí)行完成,可選地設(shè)置超時(shí)時(shí)間。run():線程的執(zhí)行入口點(diǎn),在線程啟動(dòng)時(shí)被調(diào)用。sleep(secs):線程休眠指定的秒數(shù)。
9、線程間通信
線程間通信是指在多線程編程中,多個(gè)線程之間進(jìn)行數(shù)據(jù)傳遞和共享的過(guò)程。線程間通信的目的是實(shí)現(xiàn)線程之間的協(xié)作和數(shù)據(jù)交換,以完成復(fù)雜的任務(wù)。在Python中,可以使用 queue 模塊提供的隊(duì)列來(lái)實(shí)現(xiàn)線程安全的數(shù)據(jù)傳遞。
queue 模塊提供了幾種隊(duì)列類型,常用的有以下三種:
Queue(先進(jìn)先出隊(duì)列)
Queue 是最常用的線程安全隊(duì)列,它使用先進(jìn)先出(FIFO)的方式存儲(chǔ)和獲取數(shù)據(jù)。多個(gè)線程可以安全地將數(shù)據(jù)放入隊(duì)列中,并從隊(duì)列中獲取數(shù)據(jù)。Queue 類提供了以下常用方法:
put(item[, block[, timeout]]):將數(shù)據(jù)放入隊(duì)列,可指定是否阻塞和超時(shí)時(shí)間。get([block[, timeout]]):從隊(duì)列中獲取數(shù)據(jù),可指定是否阻塞和超時(shí)時(shí)間。empty():判斷隊(duì)列是否為空。full():判斷隊(duì)列是否已滿。qsize():返回隊(duì)列中的元素?cái)?shù)量。
import queue
# 創(chuàng)建隊(duì)列對(duì)象
q = queue.Queue()
# 線程函數(shù) A
def thread_function_a():
while True:
item = q.get()
# 處理數(shù)據(jù)
# 線程函數(shù) B
def thread_function_b():
while True:
# 產(chǎn)生數(shù)據(jù)
q.put(item)在上述示例中,線程函數(shù) A從隊(duì)列中獲取數(shù)據(jù)并進(jìn)行處理,線程函數(shù) B產(chǎn)生數(shù)據(jù)并放入隊(duì)列中,兩個(gè)線程通過(guò)隊(duì)列進(jìn)行數(shù)據(jù)交換。
LifoQueue(后進(jìn)先出隊(duì)列)
LifoQueue 是一種后進(jìn)先出(LIFO)的隊(duì)列類型,與 Queue 不同的是,它的獲取順序與放入順序相反。其他方法與 Queue 類相同。
import queue # 創(chuàng)建后進(jìn)先出隊(duì)列對(duì)象 q = queue.LifoQueue()
后進(jìn)先出隊(duì)列適用于某些特定的場(chǎng)景,例如需要按照相反的順序處理數(shù)據(jù)。
PriorityQueue(優(yōu)先級(jí)隊(duì)列)
PriorityQueue 是一種根據(jù)優(yōu)先級(jí)排序的隊(duì)列類型,可以為隊(duì)列中的每個(gè)元素指定一個(gè)優(yōu)先級(jí)。優(yōu)先級(jí)高的元素先被獲取。元素的優(yōu)先級(jí)可以是數(shù)字、元組或自定義對(duì)象。其他方法與 Queue 類相同。
import queue # 創(chuàng)建優(yōu)先級(jí)隊(duì)列對(duì)象 q = queue.PriorityQueue()
優(yōu)先級(jí)隊(duì)列適用于需要根據(jù)優(yōu)先級(jí)順序處理數(shù)據(jù)的場(chǎng)景。
10、線程池
線程池是一種用于管理和復(fù)用線程的機(jī)制,可以有效地管理大量線程的生命周期,并提供簡(jiǎn)化的接口來(lái)提交和管理任務(wù)。在Python中,可以使用 concurrent.futures 模塊中的 ThreadPoolExecutor 類來(lái)創(chuàng)建線程池。
線程池的特點(diǎn)
- 線程復(fù)用:線程池中的線程可以被重復(fù)使用,避免了線程頻繁創(chuàng)建和銷毀的開銷。
- 線程管理:線程池負(fù)責(zé)管理線程的生命周期,包括線程的創(chuàng)建、銷毀和回收。
- 并發(fā)控制:線程池可以限制并發(fā)執(zhí)行的線程數(shù)量,防止系統(tǒng)資源被過(guò)度占用。
- 異步提交:線程池提供了異步提交任務(wù)的方法,可以在后臺(tái)執(zhí)行任務(wù)并返回結(jié)果。
創(chuàng)建線程池
可以使用 ThreadPoolExecutor 類來(lái)創(chuàng)建線程池??梢灾付ň€程池的大?。赏瑫r(shí)執(zhí)行的線程數(shù)量)和其他相關(guān)參數(shù)。
from concurrent.futures import ThreadPoolExecutor # 創(chuàng)建線程池 pool = ThreadPoolExecutor(max_workers=5)
在上述示例中,創(chuàng)建了一個(gè)最大同時(shí)執(zhí)行 5 個(gè)線程的線程池。
提交任務(wù)
可以使用線程池的 submit() 方法提交任務(wù),該方法會(huì)返回一個(gè) Future 對(duì)象,用于獲取任務(wù)的執(zhí)行結(jié)果。
# 定義任務(wù)函數(shù)
def task_function():
# 任務(wù)邏輯
# 提交任務(wù)到線程池
future = pool.submit(task_function)在上述示例中,將任務(wù)函數(shù) task_function 提交到線程池,并獲得了一個(gè) Future 對(duì)象。
獲取任務(wù)結(jié)果
可以使用 Future 對(duì)象的 result() 方法來(lái)獲取任務(wù)的執(zhí)行結(jié)果。如果任務(wù)尚未完成,result() 方法將會(huì)阻塞直到任務(wù)完成并返回結(jié)果。
# 獲取任務(wù)結(jié)果 result = future.result()
關(guān)閉線程池
在使用完線程池后,應(yīng)該調(diào)用 shutdown() 方法來(lái)關(guān)閉線程池。關(guān)閉線程池后,將不再接受新的任務(wù)提交,并且會(huì)等待所有已提交的任務(wù)執(zhí)行完畢后再退出。
# 關(guān)閉線程池 pool.shutdown()
以上就是Python使用thread模塊實(shí)現(xiàn)多線程的操作的詳細(xì)內(nèi)容,更多關(guān)于Python thread多線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python輸入、數(shù)據(jù)類型轉(zhuǎn)換及運(yùn)算符方式
這篇文章主要介紹了python輸入、數(shù)據(jù)類型轉(zhuǎn)換及運(yùn)算符方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
解決python3.5 正常安裝 卻不能直接使用Tkinter包的問(wèn)題
今天小編就為大家分享一篇解決python3.5 正常安裝 卻不能直接使用Tkinter包的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
Python圖像處理庫(kù)PIL的ImageFont模塊使用介紹
這篇文章主要介紹了Python圖像處理庫(kù)PIL的ImageFont模塊使用介紹,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
Python實(shí)現(xiàn)快速抓取網(wǎng)頁(yè)數(shù)據(jù)的5種高效方法
在當(dāng)今大數(shù)據(jù)時(shí)代,網(wǎng)頁(yè)數(shù)據(jù)抓取(Web Scraping)已成為獲取信息的重要手段,本文將介紹Python中5種快速抓取網(wǎng)頁(yè)數(shù)據(jù)的方法,大家可以根據(jù)需要進(jìn)行選擇2025-06-06
python的命名規(guī)則知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家分享的是關(guān)于python的命名規(guī)則知識(shí)點(diǎn)總結(jié),有需要的朋友們可以參考下。2019-10-10
opencv+圖像處理(Image Processing in OpenCV)
這篇文章主要介紹了opencv+圖像處理(Image Processing in OpenCV) 4-0改變顏色空間,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04

