再看python多線程------threading模組,pythonthreading模組

來源:互聯網
上載者:User

再看python多線程------threading模組,pythonthreading模組

現在把關於多線程的能想到的需要注意的點記錄一下:

關於threading模組:

1、關於 傳參問題

  如果調用的子線程函數需要傳參,要在參數後面加一個“,”否則會拋參數異常的錯誤。

  如下:

1     for i in xrange(5):2         threads.append(threading.Thread(target=worker,args=(i,)))

 

2、關於join()阻塞

  join()方法一旦被調用,這個線程就會被阻塞住,等其他線程執行完才執行自身。當我們在主線程A中,建立了n個子線程,這裡需要注意,根據需求是應該去阻塞父線程還是子線程,還有就是t.join()放的位置,比較下面例子:

  ---->子線程和父線程都不阻塞

 1 # -*- coding:utf-8 -*- 2 import Queue,time,threading 3 start = time.clock() 4  5 def worker(m): 6     print 'worker',m 7     time.sleep(1) 8     return 9 10 if __name__ == "__main__":11     threads = []12     for i in xrange(5):13         threads.append(threading.Thread(target=worker,args=(i,)))14     for t in threads:15         t.start()16         #t.join()  #阻塞子線程17 18     #t.join()   #阻塞父線程19 20     end = time.clock()21     print "finished: %.3fs" %(end-start)

 

得到輸入:

worker 0
worker 1
worker 2
worker 3
worker 4
finished: 0.001s

這裡,其實父線程已經結束,因為已經列印出了finished:0.001s,但是子線程並沒有執行完,sleep 1秒之後,才出現“Process finished with exit code 0”的程式結束標誌。

  

  ----->同樣代碼,當阻塞子線程時,輸出如下:

worker 0
worker 1
worker 2
worker 3
worker 4
finished: 5.004s

這裡,由於5個子線程分別剛被t.start()之後,立即把自身阻塞了,所以它們會按序執行,同樣,程式sleep了5秒,一定要注意,你這樣做,程式的效率並沒有提升,仍然需要5秒的時間。所以這樣是有問題的,問題出在t.join()代碼放的位置,應該再 for t in threads:  t.join(),使得這些線程同時start,然後同時join()。

 

  -------> 同樣代碼,當阻塞父線程時,輸出如下:

worker 0
worker 1
worker 2
worker 3
worker 4
finished: 1.003s

這裡,阻塞父線程,父線程會等待子線程結束,才會繼續運行列印finished,程式的效率也得到了提升。這相當於上面提到的,先把所有的子線程start了,再join掉。

 

3、關於setDaemon()方法

  setDaemon()方法是設定在子線程中的,當我們在父線程A中建立了n個子線程之後,給我們喜歡的子線程設定setDaemon(True)後,當它們的父線程運行結束之後,不管這些子線程運行結束還是沒結束,它會直接結束程式。這裡,還有一個需要注意的,setDaemon()方法必須設定在start()方法之前,否則會拋RuntimeError異常。

  還用上面的例子:

 1 # -*- coding:utf-8 -*- 2 import Queue,time,threading 3 start = time.clock() 4  5 def worker(m): 6     print 'worker',m 7     time.sleep(1) 8     return 9 10 if __name__ == "__main__":11     threads = []12     for i in xrange(5):13         threads.append(threading.Thread(target=worker,args=(i,)))14     for t in threads:15         t.setDaemon(True)16         t.start()17         #t.join()  #阻塞子線程18 19     #t.join()   #阻塞父線程20 21     end = time.clock()22     print "finished: %.3fs" %(end-start)

 

 這裡,沒有阻塞父線程,得到的輸出如下:

worker 0
worker 1
worker 2
worker 3
worker 4
finished: 0.001s

說明,主線程一旦結束,會直接把子線程的記憶體回收,結束整個進程的運行。子進程的sleep 1沒有執行就退出了。對於某些輔助子線程的應用情境,這個應該會有用。

 

4、建立子線程的兩種方式

   第一種是上面提到的,建立子線程要執行的函數(worker),然後把這個函數傳遞進threading.Thread的對象中,讓它來執行;

  第二種是直接從threading.Thread類繼承,建立一個新的類,通過重寫這個新的類裡面的run()方法,實現子線程要執行的內容,例如:

 1 # -*- coding:utf-8 -*- 2 __author__ = 'webber' 3 import threading,time 4  5 class Mythread(threading.Thread): 6  7     def __init__(self,m): 8         threading.Thread.__init__(self) 9         self.m = m10 11     def run(self):12         print 'worker', self.m13         time.sleep(1)14         return15 16 if __name__ == "__main__":17     start = time.clock()18 19     threads = []20     for i in xrange(5):21         threads.append(Mythread(i))22     for t in threads:23         t.start()24 25     t.join()26     end = time.clock()27     print "finished: %.3fs" % (end - start)

輸出和上面的主線程阻塞的結果一樣

這裡要注意一下黃色部分,調用的時候的傳參方式。

 

5、關於鎖----> Lock、RLock、Condition方法

  之前有提到,由於python理論上是無法實現真正意義上的多線程的,即使你有多個CPU,python的多線程也只能利用一個,那麼為了防止在多線程中對共用資料空間的資料修改時發生的尷尬,threading模組繼承了thread模組的Lock方法,這是最簡單的鎖,實現也比較簡單,只需要在子線程中修改資料前後分別加上鎖和釋放鎖即可。

就是以下三句話:

  a、主函數中建立一個鎖的對象: 例如: lock = threading.Lock()    #返回一個新的Lock對象,建立一把鎖。

  b、在子線程需要對資料進行修改之前,lock.acquire()       #擷取這把鎖

  c、在子線程對資料進行修改之後,  lock.acquire()    #釋放這把鎖

下面有個代碼應用小例子:

 1 # -*- coding:utf-8 -*- 2 __author__ = 'webber' 3 import threading, time, random 4  5 dish = 0 6 lock = threading.Lock() 7  8  9 def producerFunction():10     '''如果投的篩子比0.5大,則向盤子中增加一個蘋果'''11     global lock, dish12     while dish < 10:13         if (random.random() > 0.5):14             lock.acquire()15             dish += 116             print('生產者增加了一個蘋果,現在有%d個蘋果' % (dish,))17             lock.release()18             time.sleep(random.random() * 3)19 20 21 def consumerFunction():22     '''如果投的篩子比0.5小,則從盤子中取一個蘋果'''23     global lock, dish24     while dish > 0:25         if (random.random() < 0.5):26             lock.acquire()27             dish -= 128             print('消費者拿走一個蘋果現,現在有%d個蘋果' % (dish,))29             lock.release()30             time.sleep(random.random() * 3)31 32 33 def begin():34     ident1 = threading.Thread(target=producerFunction())35     ident2 = threading.Thread(target=consumerFunction())36     ident1.start()37     ident2.start()38 39 40 if __name__ == '__main__':41     begin()
View Code

 

  其次,threading模組提出了一個更進階的鎖RLock,它的出現是為瞭解決Lock可能會出現的死結問題,即:當由於疏忽時,可能會出現一個子線程內同一把鎖對象連續acquire()兩次,那麼由於第一次的acquire沒有release,那麼第二次的acquire請求會把該子線程掛起,導致lock對象永遠不會release,造成死結。而RLock對象允許一個線程多次對其進行acquire操作,在其內部通過counter變數維護著線程acquire的次數,而每一次的acquire操作必須有一個release操作與之對應,在所有的release操作完成之後,別的線程才能申請該RLock對象。使用上暫時我就把它當成Lock方法試了試,通過。~~~

 

  最後,threading模組提供了更進階的封裝,算是一種進階的多線程間同步方式,包括threading.Event和threading.Condition,其中,threading.Event為簡單的同步方式,一個進程標記為event,其他的進程就需要等待,用到下面幾種方法:

Event.wait([timeout]) 阻塞線程,直到Event對象內部標識位被設定為True或逾時(如果提供了參數timeout)
Event.set() 將標識號設為True
Event.clear() 設為標識符False

 

 

 

  threading.Condition 可以把Condition理解為更進階的鎖,它提供了比RLock更進階的功能,允許我們能夠控制複雜的線程同步問題,它在內部維護了一個鎖對象(預設為RLock),可以在建立Condition對象的時候把鎖對象作為參數傳入。Condition也提供了acquire和release方法,它的特色在於內部的wait和notify機制,具體可看threading模組,下面的方法只有在對象擷取到鎖之後才能調用,否則,將會拋RuntimeError異常。

Condition.wait([timeout]):  wait方法釋放內部所佔用的瑣,同時線程被掛起,直至接收到通知被喚醒或逾時(如果提供了timeout參數的話)。當線程被喚醒並重新佔有瑣的時候,程式才會繼續執行下去。 
Condition.notify() 喚醒一個掛起的線程(如果存在掛起的線程)。注意:notify()方法不會釋放所佔用的瑣。
Condition.notify_all() 喚醒所有掛起的線程(如果存在掛起的線程)。注意:這些方法不會釋放所佔用的瑣。

 

 

 

 

參考:http://orangeholic.iteye.com/blog/1720421

 

6、其他方法

  由於threading是繼承的thread模組的,所以還有些公用屬性方法,比如:

t.getName():擷取子線程的名稱,預設為:Tread-n (n為線程式列號)

t.setName():設定子線程的名稱

t.ident:擷取線程標識符,要在t.start()之後調用才有效,否則返回None

t.is_alive():判斷子線程是否啟用,返回True或False

 

關於Semaphore、event、Condition的具體執行個體,沒再去嘗試,以後遇到再試,可參考這篇部落格:

    http://www.jb51.net/article/57672.htm

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.