程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> Python中線程編程之threading模塊的使用詳解

Python中線程編程之threading模塊的使用詳解

編輯:更多關於編程

       這篇文章主要介紹了Python中線程編程之threading模塊的使用詳解,由於GIL的存在,線程一直是Python編程中的焦點問題,需要的朋友可以參考下

      threading.Thread

      Thread 是threading模塊中最重要的類之一,可以使用它來創建線程。有兩種方式來創建線程:一種是通過繼承Thread類,重寫它的run方法;另一種是創建一個threading.Thread對象,在它的初始化函數(__init__)中將可調用對象作為參數傳入。下面分別舉例說明。先來看看通過繼承threading.Thread類來創建線程的例子:

      ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #coding=gbk import threading, time, random count = 0 class Counter(threading.Thread): def __init__(self, lock, threadName):   '''@summary: 初始化對象。     @param lock: 瑣對象。   @param threadName: 線程名稱。   ''' super(Counter, self).__init__(name = threadName) #注意:一定要顯式的調用父類的初始 化函數。 self.lock = lock   def run(self):   '''@summary: 重寫父類run方法,在線程啟動後執行該方法內的代碼。   ''' global count self.lock.acquire() for i in xrange(10000): count = count + 1 self.lock.release() lock = threading.Lock() for i in range(5): Counter(lock, "thread-" + str(i)).start() time.sleep(2) #確保線程都執行完畢 print count

      在代碼中,我們創建了一個Counter類,它繼承了threading.Thread。初始化函數接收兩個參數,一個是瑣對象,另一個是線程的名稱。在Counter中,重寫了從父類繼承的run方法,run方法將一個全局變量逐一的增加10000。在接下來的代碼中,創建了五個Counter對象,分別調用其start方法。最後打印結果。這裡要說明一下run方法 和start方法: 它們都是從Thread繼承而來的,run()方法將在線程開啟後執行,可以把相關的邏輯寫到run方法中(通常把run方法稱為活動[Activity]。);start()方法用於啟動線程。

      再看看另外一種創建線程的方法:

      ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import threading, time, random count = 0 lock = threading.Lock() def doAdd():   '''@summary: 將全局變量count 逐一的增加10000。   ''' global count, lock lock.acquire() for i in xrange(10000): count = count + 1 lock.release() for i in range(5): threading.Thread(target = doAdd, args = (), name = 'thread-' + str(i)).start() time.sleep(2) #確保線程都執行完畢 print count

      在這段代碼中,我們定義了方法doAdd,它將全局變量count 逐一的增加10000。然後創建了5個Thread對象,把函數對象doAdd 作為參數傳給它的初始化函數,再調用Thread對象的start方法,線程啟動後將執行doAdd函數。這裡有必要介紹一下threading.Thread類的初始化函數原型:

      ?

    1 def __init__(self, group=None, target=None, name=None, args=(), kwargs={})

      參數group是預留的,用於將來擴展;

      參數target是一個可調用對象(也稱為活動[activity]),在線程啟動後執行;

      參數name是線程的名字。默認值為“Thread-N“,N是一個數字。

      參數args和kwargs分別表示調用target時的參數列表和關鍵字參數。

      Thread類還定義了以下常用方法與屬性:

      Thread.getName()

      Thread.setName()

      Thread.name

      用於獲取和設置線程的名稱。

      Thread.ident

      獲取線程的標識符。線程標識符是一個非零整數,只有在調用了start()方法之後該屬性才有效,否則它只返回None。

      Thread.is_alive()

      Thread.isAlive()

      判斷線程是否是激活的(alive)。從調用start()方法啟動線程,到run()方法執行完畢或遇到未處理異常而中斷 這段時間內,線程是激活的。

      Thread.join([timeout])

      調用Thread.join將會使主調線程堵塞,直到被調用線程運行結束或超時。參數timeout是一個數值類型,表示超時時間,如果未提供該參數,那麼主調線程將一直堵塞到被調線程結束。下面舉個例子說明join()的使用:

      ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 import threading, time def doWaiting(): print 'start waiting:', time.strftime('%H:%M:%S') time.sleep(3) print 'stop waiting', time.strftime('%H:%M:%S') thread1 = threading.Thread(target = doWaiting) thread1.start() time.sleep(1) #確保線程thread1已經啟動 print 'start join' thread1.join() #將一直堵塞,直到thread1運行結束。 print 'end join' threading.RLock和threading.Lock

      在threading模塊中,定義兩種類型的瑣:threading.Lock和threading.RLock。它們之間有一點細微的區別,通過比較下面兩段代碼來說明:

      ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import threading lock = threading.Lock() #Lock對象 lock.acquire() lock.acquire() #產生了死瑣。 lock.release() lock.release()   import threading rLock = threading.RLock() #RLock對象 rLock.acquire() rLock.acquire() #在同一線程內,程序不會堵塞。 rLock.release() rLock.release()

      這兩種瑣的主要區別是:RLock允許在同一線程中被多次acquire。而Lock卻不允許這種情況。注意:如果使用RLock,那麼acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所占用的瑣。

      threading.Condition

      可以把Condiftion理解為一把高級的瑣,它提供了比Lock, RLock更高級的功能,允許我們能夠控制復雜的線程同步問題。threadiong.Condition在內部維護一個瑣對象(默認是RLock),可以在創建Condigtion對象的時候把瑣對象作為參數傳入。Condition也提供了acquire, release方法,其含義與瑣的acquire, release方法一致,其實它只是簡單的調用內部瑣對象的對應的方法而已。Condition還提供了如下方法(特別要注意:這些方法只有在占用瑣(acquire)之後才能調用,否則將會報RuntimeError異常。):

      Condition.wait([timeout]):

      wait方法釋放內部所占用的瑣,同時線程被掛起,直至接收到通知被喚醒或超時(如果提供了timeout參數的話)。當線程被喚醒並重新占有瑣的時候,程序才會繼續執行下去。

      Condition.notify():

      喚醒一個掛起的線程(如果存在掛起的線程)。注意:notify()方法不會釋放所占用的瑣。

      Condition.notify_all()

      Condition.notifyAll()

      喚醒所有掛起的線程(如果存在掛起的線程)。注意:這些方法不會釋放所占用的瑣。

      現在寫個捉迷藏的游戲來具體介紹threading.Condition的基本使用。假設這個游戲由兩個人來玩,一個藏(Hider),一個找(Seeker)。游戲的規則如下:1. 游戲開始之後,Seeker先把自己眼睛蒙上,蒙上眼睛後,就通知Hider;2. Hider接收通知後開始找地方將自己藏起來,藏好之後,再通知Seeker可以找了; 3. Seeker接收到通知之後,就開始找Hider。Hider和Seeker都是獨立的個體,在程序中用兩個獨立的線程來表示,在游戲過程中,兩者之間的行為有一定的時序關系,我們通過Condition來控制這種時序關系。

      ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #---- Condition #---- 捉迷藏的游戲 import threading, time class Hider(threading.Thread): def __init__(self, cond, name): super(Hider, self).__init__() self.cond = cond self.name = name   def run(self): time.sleep(1) #確保先運行Seeker中的方法   self.cond.acquire() #b print self.name + ': 我已經把眼睛蒙上了' self.cond.notify() self.cond.wait() #c   #f print self.name + ': 我找到你了 ~_~' self.cond.notify() self.cond.release()   #g print self.name + ': 我贏了' #h   class Seeker(threading.Thread): def __init__(self, cond, name): super(Seeker, self).__init__() self.cond = cond self.name = name def run(self): self.cond.acquire() self.cond.wait() #a #釋放對瑣的占用,同時線程掛起在這裡,直到被notify並重新占 有瑣。   #d print self.name + ': 我已經藏好了,你快來找我吧' self.cond.notify() self.cond.wait() #e   #h self.cond.release() print self.name + ': 被你找到了,哎~~~'   cond = threading.Condition() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') seeker.start() hider.start() threading.Event

      Event實現與Condition類似的功能,不過比Condition簡單一點。它通過維護內部的標識符來實現線程間的同步問題。(threading.Event和.NET中的System.Threading.ManualResetEvent類實現同樣的功能。)

      Event.wait([timeout])

      堵塞線程,直到Event對象內部標識位被設為True或超時(如果提供了參數timeout)。

      Event.set()

      將標識位設為Ture

      Event.clear()

      將標識伴設為False。

      Event.isSet()

      判斷標識位是否為Ture。

      下面使用Event來實現捉迷藏的游戲(可能用Event來實現不是很形象)

      ?

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #---- Event #---- 捉迷藏的游戲 import threading, time class Hider(threading.Thread): def __init__(self, cond, name): super(Hider, self).__init__() self.cond = cond self.name = name   def run(self): time.sleep(1) #確保先運行Seeker中的方法   print self.name + ': 我已經把眼睛蒙上了'   self.cond.set()   time.sleep(1)   self.cond.wait() print self.name + ': 我找到你了 ~_~'   self.cond.set()   print self.name + ': 我贏了'   class Seeker(threading.Thread): def __init__(self, cond, name): super(Seeker, self).__init__() self.cond = cond self.name = name def run(self): self.cond.wait()   print self.name + ': 我已經藏好了,你快來找我吧' self.cond.set()   time.sleep(1) self.cond.wait()   print self.name + ': 被你找到了,哎~~~'   cond = threading.Event() seeker = Seeker(cond, 'seeker') hider = Hider(cond, 'hider') seeker.start() hider.start() threading.Timer

      threading.Timer是threading.Thread的子類,可以在指定時間間隔後執行某個操作。下面是Python手冊上提供的一個例子:

      ?

    1 2 3 4 5 def hello(): print "hello, world" t = Timer(3, hello) t.start() # 3秒鐘之後執行hello函數。

      threading模塊中還有一些常用的方法沒有介紹:

      threading.active_count()

      threading.activeCount()

      獲取當前活動的(alive)線程的個數。

      threading.current_thread()

      threading.currentThread()

      獲取當前的線程對象(Thread object)。

      threading.enumerate()

      獲取當前所有活動線程的列表。

      threading.settrace(func)

      設置一個跟蹤函數,用於在run()執行之前被調用。

      threading.setprofile(func)

      設置一個跟蹤函數,用於在run()執行完畢之後調用。

      threading模塊的內容很多,一篇文章很難寫全,更多關於threading模塊的信息,請查詢Python手冊 threading模塊。

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved