Python多執行緒程式設計


同時執行多個執行緒類似於同時執行多個不同的程式,但具有以下好處 -

  • 進程內的多個執行緒與主執行緒共用相同的資料空間,因此可以比單獨的進程更容易地共用資訊或彼此進行通訊。

  • 執行緒有時也被稱為輕量級進程,它們不需要太多的記憶體開銷; 它們比進程便宜。

執行緒有一個開始,執行順序和終止。 它有一個指令指標,可以跟蹤其上下文中當前執行的位置。

  • 它可以被搶占(中斷)。
  • 當其他執行緒正在執行時,它可以臨時保留(也稱為睡眠) - 這稱為讓步。

有兩種不同的執行緒 -

  • 核心執行緒
  • 使用者執行緒

核心執行緒是作業系統的一部分,而使用者空間執行緒未在核心中實現。

有兩個模組用於支援在Python 3中使用執行緒 -

  • _thread
  • threading

thread模組已被「不推薦」了很長一段時間。 鼓勵使用者使用threading模組。 因此,在Python 3中,thread模組不再可用。 但是,thread模組已被重新命名為「_thread」,用於Python 3中的向後相容性。

1.啟動新執行緒

要產生/啟動一個執行緒,需要呼叫thread模組中的以下方法 -

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

這種方法呼叫可以快速有效地在Linux和Windows中建立新的執行緒。

方法呼叫立即返回,子執行緒啟動並使用傳遞的args列表呼叫函式。當函式返回時,執行緒終止。

在這裡,args是一個元組的引數; 使用空的元組來呼叫函式表示不傳遞任何引數。 kwargs是關鍵字引數的可選字典。

範例

#!/usr/bin/python3

import _thread
import time

# Define a function for the thread
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

# Create two threads as follows
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

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

F:\worksp\python>python thread_start.py
Thread-1: Tue Jun 27 03:06:09 2018
Thread-2: Tue Jun 27 03:06:11 2018
Thread-1: Tue Jun 27 03:06:11 2018
Thread-1: Tue Jun 27 03:06:13 2018
Thread-2: Tue Jun 27 03:06:15 2018
Thread-1: Tue Jun 27 03:06:15 2018

程式進入無限迴圈,可通過按ctrl-c停止或退出。雖然它對於低階執行緒非常有效,但與較新的執行緒模組相比,thread模組非常有限。

2. threading模組

Python 2.4中包含的較新的執行緒模組為執行緒提供了比上面討論的執行緒模組更強大的高階支援。
執行緒模組公開了執行緒模組的所有方法,並提供了一些其他方法 -

  • threading.activeCount() - 返回活動的執行緒物件的數量。
  • threading.currentThread() - 返回撥用者執行緒控制元件中執行緒物件的數量。
  • threading.enumerate() - 返回當前處於活動狀態的所有執行緒物件的列表。

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

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

3.使用threading模組建立執行緒

要使用threading模組實現新執行緒,必須執行以下操作:

  • 定義Thread類的新子類。
  • 覆蓋__init __(self [,args])方法新增其他引數。
  • 然後,重寫run(self [,args])方法來實現執行緒在啟動時應該執行的操作。

當建立了新的Thread的子類之後,就可以建立一個範例,然後呼叫start()方法來呼叫run()方法來啟動一個新的執行緒。

範例

#!/usr/bin/python3

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

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

# Start new Threads
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")

當執行上述程式時,它會產生以下結果 -

Starting Thread-1
Starting Thread-2
Thread-1: Tue Jun 27 03:19:43 2017
Thread-2: Tue Jun 27 03:19:44 2017
Thread-1: Tue Jun 27 03:19:44 2017
Thread-1: Tue Jun 27 03:19:45 2017
Thread-2: Tue Jun 27 03:19:46 2017
Thread-1: Tue Jun 27 03:19:46 2017
Thread-1: Tue Jun 27 03:19:47 2017
Exiting Thread-1
Thread-2: Tue Jun 27 03:19:48 2017
Thread-2: Tue Jun 27 03:19:50 2017
Thread-2: Tue Jun 27 03:19:52 2017
Exiting Thread-2
Exiting Main Thread

4.同步執行緒

Python提供的threading模組包括一個簡單易用的鎖定機制,允許同步執行緒。 通過呼叫lock()方法建立一個新的鎖,該方法返回新的鎖。

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

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

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

範例

#!/usr/bin/python3
# save file : MyThread2.py
import threading
import time

class MyThread2 (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 = MyThread2(1, "Thread-1", 1)
thread2 = MyThread2(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: Tue Jun 27 03:51:45 2017
Thread-1: Tue Jun 27 03:51:46 2017
Thread-1: Tue Jun 27 03:51:47 2017
Thread-2: Tue Jun 27 03:51:49 2017
Thread-2: Tue Jun 27 03:51:51 2017
Thread-2: Tue Jun 27 03:51:53 2017
Exiting Main Thread

5.多執行緒優先順序佇列

queue模組允許建立一個新的佇列物件,可以容納特定數量的專案。 有以下方法來控制佇列 -

  • get() - get()從佇列中刪除並返回一個專案。
  • put() - put()將項新增到佇列中。
  • qsize() - qsize()返回當前佇列中的專案數。
  • empty() - 如果佇列為空,則empty()方法返回True; 否則返回False
  • full() - 如果佇列已滿,則full()方法返回True; 否則返回False

範例

#!/usr/bin/python3
#coding=utf-8

import queue
import threading
import time

exitFlag = 0

class MyQueue (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 = MyQueue(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-3 processing One
Thread-3 processing Two
Thread-3 processing Three
Thread-3 processing Four
Thread-3 processing Five
Exiting Thread-1
Exiting Thread-2
Exiting Thread-3
Exiting Main Thread