執行緒的實現


在本章中,我們將學習如何在Python中實現執行緒。

用於執行緒實現的Python模組

Python執行緒有時稱為輕量級進程,因為執行緒比進程佔用的記憶體少得多。 執行緒允許一次執行多個任務。 在Python中,以下兩個模組在一個程式中實現執行緒 -

  • _thread 模組
  • threading 模組

這兩個模組之間的主要區別在於_thread模組將執行緒視為一個函式,而threading模組將每個執行緒視為一個物件並以物件導向的方式實現它。 此外,_thread模組在低階執行緒中有效並且比threading模組具有更少的功能。

_thread 模組

在Python的早期版本中,擁有thread模組,但在相當長的一段時間裡它已被視為「已棄用」。 鼓勵使用者改用threading模組。 因此,在Python 3中,thread模組不再可用。 它已被重新命名為_thread,用於Python3中的向後不相容。

為了在_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()來啟動一個新執行緒,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步 - 匯入必要的模組,threadingtime

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中,可以通過不同的方式啟動一個新的執行緒,但其中最簡單的一個就是將其定義為一個單一的函式。 在定義函式之後,可以將它作為新執行緒的目標。執行緒物件等等。 執行下面的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。 我們可以通過以下輸出來了解nondaemondaemon執行緒之間的區別 -

Hello

starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello