執行緒的實現



本章我們將學習如何在 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() - 此方法返回當前活動的所有執行緒物件的列表。

  • 為了實現執行緒,<threading> 模組具有 Thread 類,該類提供以下方法:

    • run() - run() 方法是執行緒的入口點。

    • start() - start() 方法透過呼叫 run 方法來啟動執行緒。

    • join([time]) - join() 等待執行緒終止。

    • isAlive() - isAlive() 方法檢查執行緒是否仍在執行。

    • getName() - getName() 方法返回執行緒的名稱。

    • setName() - setName() 方法設定執行緒的名稱。

如何使用 <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
廣告