Python - 多執行緒



在 Python 中,多執行緒允許你在單個程序中併發執行多個執行緒,這也稱為基於執行緒的並行性。這意味著程式可以同時執行多個任務,從而提高效率和響應速度。

Python 中的多執行緒尤其適用於多個 I/O 繫結操作,而不是需要大量計算的任務。

通常,計算機程式按順序執行指令,從開始到結束。而多執行緒將主要任務劃分為多個子任務,並以重疊的方式執行它們。

與程序的比較

作業系統能夠併發處理多個程序。它為每個程序分配單獨的記憶體空間,以便一個程序無法訪問或寫入其他程序的空間。

另一方面,執行緒可以被認為是單個程式中共享分配給它的記憶體空間的輕量級子程序,這使得通訊和資料共享更容易。由於它們是輕量級的並且不需要大量的記憶體開銷;它們比程序更經濟。

multithreading

程序總是從單個執行緒(主執行緒)開始。根據需要,可以啟動一個新執行緒,並將子任務委派給它。現在這兩個執行緒以重疊的方式工作。當分配給輔助執行緒的任務完成後,它將與主執行緒合併。

執行緒有開始、執行序列和結束。它有一個指令指標,用於跟蹤它當前在其上下文中執行的位置。

  • 它可以被搶佔(中斷)

  • 它可以暫時被掛起(也稱為休眠),而其他執行緒正在執行——這稱為讓步。

Python 中的執行緒處理模組

Python 的標準庫提供了兩個主要的模組來管理執行緒:_threadthreading

_thread 模組

_thread 模組,也稱為低階執行緒模組,自 Python 2 版本以來一直是 Python 標準庫的一部分。它提供了一個基本的執行緒管理 API,支援在共享全域性資料空間中併發執行執行緒。該模組包含用於同步目的的簡單鎖(互斥鎖)。

threading 模組

threading 模組於 Python 2.4 中引入,它基於 _thread 提供更高階、更全面的執行緒 API。它提供了強大的執行緒管理工具,使在 Python 應用程式中使用執行緒更容易。

threading 模組的關鍵特性

threading 模組公開了 thread 模組的所有方法,並提供了一些附加方法:

  • threading.activeCount() - 返回活動執行緒物件的數目。
  • threading.currentThread() - 返回呼叫方執行緒控制中的執行緒物件數目。
  • threading.enumerate() - 返回當前活動的所有執行緒物件的列表。

除了這些方法外,threading 模組還有一個實現 threading 的 Thread 類。Thread 類提供的方法如下:

  • run() - run() 方法是執行緒的入口點。
  • start() - start() 方法透過呼叫 run 方法來啟動執行緒。
  • join([time]) - join() 方法等待執行緒終止。
  • isAlive() - isAlive() 方法檢查執行緒是否仍在執行。
  • getName() - getName() 方法返回執行緒的名稱。
  • setName() - setName() 方法設定執行緒的名稱。

啟動新執行緒

要在 Python 中建立和啟動新執行緒,可以使用低階 _thread 模組或高階 threading 模組。由於其附加功能和易用性,通常推薦使用 threading 模組。下面,您可以看到兩種方法。

使用 _thread 模組啟動新執行緒

_thread 模組的 start_new_thread() 方法提供了一種建立和啟動新執行緒的基本方法。此方法提供了一種在 Linux 和 Windows 中快速有效地建立新執行緒的方法。以下是該方法的語法:

thread.start_new_thread(function, args[, kwargs] )

此方法呼叫立即返回,新執行緒開始使用給定的引數執行指定的函式。函式返回時,執行緒終止。

示例

此示例演示如何使用 _thread 模組建立和執行執行緒。每個執行緒都使用不同的引數執行 print_name 函式。time.sleep(0.5) 呼叫確保主程式在退出之前等待執行緒完成執行。

import _thread
import time

def print_name(name, *arg):
   print(name, *arg)

name="Tutorialspoint..."
_thread.start_new_thread(print_name, (name, 1))
_thread.start_new_thread(print_name, (name, 1, 2))

time.sleep(0.5)

執行上述程式碼時,會產生以下結果:

Tutorialspoint... 1
Tutorialspoint... 1 2

雖然它對於低階執行緒非常有效,但與提供更多功能和更高階執行緒管理的 threading 模組相比,_thread 模組的功能有限。

使用 threading 模組啟動新執行緒

threading 模組提供 Thread 類,用於建立和管理執行緒。

以下是使用 threading 模組啟動新執行緒的幾個步驟:

  • 建立一個希望執行緒執行的函式。
  • 然後,透過傳遞目標函式及其引數,使用 Thread 類建立一個 Thread 物件。
  • 呼叫 Thread 物件上的 start 方法開始執行。
  • 可選:呼叫 join 方法以等待執行緒完成,然後再繼續執行。

示例

以下示例演示如何使用 threading 模組建立和啟動執行緒。它執行一個 print_name 函式,該函式列印名稱以及一些引數。此示例建立兩個執行緒,使用 start() 方法啟動它們,並使用 join 方法等待它們完成。

import threading
import time

def print_name(name, *args):
    print(name, *args)

name = "Tutorialspoint..."

# Create and start threads
thread1 = threading.Thread(target=print_name, args=(name, 1))
thread2 = threading.Thread(target=print_name, args=(name, 1, 2))

thread1.start()
thread2.start()

# Wait for threads to complete
thread1.join()
thread2.join()

print("Threads are finished...exiting")

執行上述程式碼時,會產生以下結果:

Tutorialspoint... 1
Tutorialspoint... 1 2
Threads are finished...exiting

執行緒同步

Python 提供的 threading 模組包含一個易於實現的鎖定機制,允許你同步執行緒。透過呼叫 Lock() 方法建立一個新的鎖,該方法返回新的鎖。

新鎖物件的 acquire(blocking) 方法用於強制執行緒同步執行。可選的 blocking 引數允許你控制執行緒是否等待獲取鎖。

如果 blocking 設定為 0,則如果無法獲取鎖,執行緒立即返回 0 值;如果獲取了鎖,則返回 1。如果 blocking 設定為 1,則執行緒阻塞並等待鎖釋放。

新鎖物件的 release() 方法用於在不再需要鎖時釋放鎖。

示例

import threading
import time

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)
      # Get lock to synchronize threads
      threadLock.acquire()
      print_time(self.name, self.counter, 3)
      # Free lock to release next thread
      threadLock.release()

def print_time(threadName, delay, counter):
   while counter:
      time.sleep(delay)
      print ("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1

threadLock = threading.Lock()
threads = []

# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads
thread1.start()
thread2.start()

# Add threads to thread list
threads.append(thread1)
threads.append(thread2)

# Wait for all threads to complete
for t in threads:
    t.join()
print ("Exiting Main Thread")

執行上述程式碼時,會產生以下結果:

Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread

多執行緒優先順序佇列

Queue 模組允許你建立一個可以容納特定數量項的新佇列物件。以下是控制佇列的方法:

  • get() - get() 從佇列中移除並返回一個項。

  • put() - put() 將項新增到佇列。

  • qsize() - qsize() 返回當前在佇列中的項數。

  • empty() - empty() 如果佇列為空則返回 True;否則返回 False。

  • full() - full() 如果佇列已滿則返回 True;否則返回 False。

示例

import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, q):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.q = q
   def run(self):
      print ("Starting " + self.name)
      process_data(self.name, self.q)
      print ("Exiting " + self.name)

def process_data(threadName, q):
   while not exitFlag:
      queueLock.acquire()
      if not workQueue.empty():
         data = q.get()
         queueLock.release()
         print ("%s processing %s" % (threadName, data))
      else:
         queueLock.release()
         time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# Create new threads
for tName in threadList:
   thread = myThread(threadID, tName, workQueue)
   thread.start()
   threads.append(thread)
   threadID += 1

# Fill the queue
queueLock.acquire()
for word in nameList:
   workQueue.put(word)
queueLock.release()

# Wait for queue to empty
while not workQueue.empty():
   pass

# Notify threads it's time to exit
exitFlag = 1

# Wait for all threads to complete
for t in threads:
   t.join()
print ("Exiting Main Thread")

執行上述程式碼時,會產生以下結果:

Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread
廣告