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