
執行緒的實現
本章我們將學習如何在 Python 中實現執行緒。
Python 執行緒實現模組
Python 執行緒有時被稱為輕量級程序,因為執行緒佔用的記憶體遠小於程序。執行緒允許同時執行多個任務。在 Python 中,我們有兩個實現程式中執行緒的模組:
<_thread> 模組
<threading> 模組
這兩個模組的主要區別在於,<_thread> 模組將執行緒視為函式,而 <threading> 模組將每個執行緒視為物件,並以面向物件的方式實現它。此外,<_thread> 模組在低階執行緒中有效,並且功能比 <threading> 模組少。
<_thread> 模組
在早期版本的 Python 中,我們有 <thread> 模組,但它已被認為是“已棄用”很長時間了。鼓勵使用者改用 <threading> 模組。因此,在 Python 3 中,"thread" 模組不再可用。為了向後相容 Python 3,它已被重新命名為 "<_thread>"。
要藉助 <_thread> 模組生成新執行緒,我們需要呼叫它的 start_new_thread 方法。此方法的工作原理可以透過以下語法來理解:
_thread.start_new_thread ( function, args[, kwargs] )
這裡:
args 是引數的元組
kwargs 是可選的關鍵字引數字典
如果我們想在不傳遞引數的情況下呼叫函式,則需要在 args 中使用空的元組引數。
此方法呼叫立即返回,子執行緒啟動並使用傳遞的 args 列表(如果有)呼叫函式。執行緒在函式返回時終止。
示例
以下是使用 <_thread> 模組生成新執行緒的示例。我們在這裡使用 start_new_thread() 方法。
import _thread import time def print_time( threadName, delay): count = 0 while count < 5: time.sleep(delay) count += 1 print ("%s: %s" % ( threadName, time.ctime(time.time()) )) try: _thread.start_new_thread( print_time, ("Thread-1", 2, ) ) _thread.start_new_thread( print_time, ("Thread-2", 4, ) ) except: print ("Error: unable to start thread") while 1: pass
輸出
以下輸出將幫助我們理解如何使用 <_thread> 模組生成新執行緒。
Thread-1: Mon Apr 23 10:03:33 2018 Thread-2: Mon Apr 23 10:03:35 2018 Thread-1: Mon Apr 23 10:03:35 2018 Thread-1: Mon Apr 23 10:03:37 2018 Thread-2: Mon Apr 23 10:03:39 2018 Thread-1: Mon Apr 23 10:03:39 2018 Thread-1: Mon Apr 23 10:03:41 2018 Thread-2: Mon Apr 23 10:03:43 2018 Thread-2: Mon Apr 23 10:03:47 2018 Thread-2: Mon Apr 23 10:03:51 2018
<threading> 模組
<threading> 模組以面向物件的方式實現,並將每個執行緒視為物件。因此,它比 <_thread> 模組提供了更強大、更高層次的執行緒支援。此模組包含在 Python 2.4 中。
<threading> 模組中的附加方法
<threading> 模組包含 <_thread> 模組的所有方法,但它也提供其他方法。附加方法如下:
threading.activeCount() - 此方法返回活動執行緒物件的數目
threading.currentThread() - 此方法返回呼叫方執行緒控制中的執行緒物件的數目。
threading.enumerate() - 此方法返回當前活動的所有執行緒物件的列表。
run() - run() 方法是執行緒的入口點。
start() - start() 方法透過呼叫 run 方法來啟動執行緒。
join([time]) - join() 等待執行緒終止。
isAlive() - isAlive() 方法檢查執行緒是否仍在執行。
getName() - getName() 方法返回執行緒的名稱。
setName() - setName() 方法設定執行緒的名稱。
為了實現執行緒,<threading> 模組具有 Thread 類,該類提供以下方法:
如何使用 <threading> 模組建立執行緒?
在本節中,我們將學習如何使用 <threading> 模組建立執行緒。請按照以下步驟使用 <threading> 模組建立新執行緒:
步驟 1 - 在此步驟中,我們需要定義 Thread 類的新的子類。
步驟 2 - 然後,為了新增其他引數,我們需要重寫 __init__(self [,args]) 方法。
步驟 3 - 在此步驟中,我們需要重寫 run(self [,args]) 方法來實現執行緒啟動時應執行的操作。
現在,建立新的 Thread 子類後,我們可以建立它的例項,然後透過呼叫 start() 啟動新執行緒,這反過來又會呼叫 run() 方法。
示例
請考慮以下示例,瞭解如何使用 <threading> 模組生成新執行緒。
import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Starting " + self.name) print_time(self.name, self.counter, 5) print ("Exiting " + self.name) def print_time(threadName, delay, counter): while counter: if exitFlag: threadName.exit() time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) thread1.start() thread2.start() thread1.join() thread2.join() print ("Exiting Main Thread") Starting Thread-1 Starting Thread-2
輸出
現在,請考慮以下輸出:
Thread-1: Mon Apr 23 10:52:09 2018 Thread-1: Mon Apr 23 10:52:10 2018 Thread-2: Mon Apr 23 10:52:10 2018 Thread-1: Mon Apr 23 10:52:11 2018 Thread-1: Mon Apr 23 10:52:12 2018 Thread-2: Mon Apr 23 10:52:12 2018 Thread-1: Mon Apr 23 10:52:13 2018 Exiting Thread-1 Thread-2: Mon Apr 23 10:52:14 2018 Thread-2: Mon Apr 23 10:52:16 2018 Thread-2: Mon Apr 23 10:52:18 2018 Exiting Thread-2 Exiting Main Thread
Python 程式用於各種執行緒狀態
執行緒有五種狀態 - 新建、可執行、執行、等待和死亡。在這五種狀態中,我們將主要關注三種狀態 - 執行、等待和死亡。執行緒在其執行狀態下獲取其資源,在其等待狀態下等待資源;如果正在執行並已獲取,則最終釋放資源處於死亡狀態。
以下 Python 程式將藉助 start()、sleep() 和 join() 方法顯示執行緒分別進入執行、等待和死亡狀態的方式。
步驟 1 - 匯入必要的模組 <threading> 和 <time>
import threading import time
步驟 2 - 定義一個函式,該函式將在建立執行緒時呼叫。
def thread_states(): print("Thread entered in running state")
步驟 3 - 我們使用 time 模組的 sleep() 方法使我們的執行緒等待例如 2 秒。
time.sleep(2)
步驟 4 - 現在,我們正在建立一個名為 T1 的執行緒,它採用上面定義的函式的引數。
T1 = threading.Thread(target=thread_states)
步驟 5 - 現在,藉助 start() 函式,我們可以啟動我們的執行緒。它將生成我們定義函式時設定的訊息。
T1.start() Thread entered in running state
步驟 6 - 現在,最後,在它完成執行後,我們可以使用 join() 方法終止執行緒。
T1.join()
在 Python 中啟動執行緒
在 Python 中,我們可以透過不同的方式啟動新執行緒,但其中最簡單的一種是將其定義為單個函式。定義函式後,我們可以將其作為新 threading.Thread 物件的目標等等。執行以下 Python 程式碼以瞭解該函式的工作原理:
import threading import time import random def Thread_execution(i): print("Execution of Thread {} started\n".format(i)) sleepTime = random.randint(1,4) time.sleep(sleepTime) print("Execution of Thread {} finished".format(i)) for i in range(4): thread = threading.Thread(target=Thread_execution, args=(i,)) thread.start() print("Active Threads:" , threading.enumerate())
輸出
Execution of Thread 0 started Active Threads: [<_MainThread(MainThread, started 6040)>, <HistorySavingThread(IPythonHistorySavingThread, started 5968)>, <Thread(Thread-3576, started 3932)>] Execution of Thread 1 started Active Threads: [<_MainThread(MainThread, started 6040)>, <HistorySavingThread(IPythonHistorySavingThread, started 5968)>, <Thread(Thread-3576, started 3932)>, <Thread(Thread-3577, started 3080)>] Execution of Thread 2 started Active Threads: [<_MainThread(MainThread, started 6040)>, <HistorySavingThread(IPythonHistorySavingThread, started 5968)>, <Thread(Thread-3576, started 3932)>, <Thread(Thread-3577, started 3080)>, <Thread(Thread-3578, started 2268)>] Execution of Thread 3 started Active Threads: [<_MainThread(MainThread, started 6040)>, <HistorySavingThread(IPythonHistorySavingThread, started 5968)>, <Thread(Thread-3576, started 3932)>, <Thread(Thread-3577, started 3080)>, <Thread(Thread-3578, started 2268)>, <Thread(Thread-3579, started 4520)>] Execution of Thread 0 finished Execution of Thread 1 finished Execution of Thread 2 finished Execution of Thread 3 finished
Python 中的守護執行緒
在 Python 中實現守護執行緒之前,我們需要了解守護執行緒及其用途。在計算方面,守護程序是一個後臺程序,它處理各種服務的請求,例如資料傳送、檔案傳輸等。如果不再需要它,它將處於休眠狀態。也可以藉助非守護執行緒完成相同的任務。但是,在這種情況下,主執行緒必須手動跟蹤非守護執行緒。另一方面,如果我們使用守護執行緒,則主執行緒可以完全忘記這一點,並且當主執行緒退出時它將被終止。關於守護執行緒的另一個重要點是,我們可以選擇僅將它們用於非關鍵任務,如果這些任務未完成或中途被終止,則不會影響我們。以下是 Python 中守護執行緒的實現:
import threading import time def nondaemonThread(): print("starting my thread") time.sleep(8) print("ending my thread") def daemonThread(): while True: print("Hello") time.sleep(2) if __name__ == '__main__': nondaemonThread = threading.Thread(target = nondaemonThread) daemonThread = threading.Thread(target = daemonThread) daemonThread.setDaemon(True) daemonThread.start() nondaemonThread.start()
在上面的程式碼中,有兩個函式,即 >nondaemonThread() 和 >daemonThread()。第一個函式列印其狀態並在 8 秒後休眠,而 deamonThread() 函式每 2 秒無限期地列印 Hello。我們可以藉助以下輸出瞭解非守護執行緒和守護執行緒之間的區別:
Hello starting my thread Hello Hello Hello Hello ending my thread Hello Hello Hello Hello Hello