本文實(shí)例分析了python多線程用法。分享給大家供大家參考。具體如下:
今天在學(xué)習(xí)嘗試學(xué)習(xí)python多線程的時(shí)候,突然發(fā)現(xiàn)自己一直對(duì)super的用法不是很清楚,所以先總結(jié)一些遇到的問題。當(dāng)我嘗試編寫下面的代碼的時(shí)候:
復(fù)制代碼 代碼如下:class A():
def __init__( self ):
print "A"
class B( A ):
def __init__( self ):
super( B, self ).__init__( )
# A.__init__( self )
print "B"
b = B()
出現(xiàn):
super( B, self ).__init__()
TypeError: must be type, not classobj
最后發(fā)現(xiàn)原來是python中的新式類的問題,也就是A必須是新式類。解決方法如下兩種:
(1)
復(fù)制代碼 代碼如下:class A( object ):
def __init__( self ):
print "A"
class B( A ):
def __init__( self ):
super( B, self ).__init__( )
# A.__init__( self ) ##這條語句是舊式的,存在潛在的問題,應(yīng)該避免使用
print "B"
b = B()
(2)
復(fù)制代碼 代碼如下:__metaclass__=type
class A():
def __init__( self ):
print "A"
class B( A ):
def __init__( self ):
super( B, self ).__init__( )
# A.__init__( self ) ##這條語句是舊式的,存在潛在的問題,應(yīng)該避免使用
print "B"
b = B()
注意:如果在super( B, self ).__init__( )
語句中添加self,也就是super( B, self ).__init__( self ),會(huì)出現(xiàn)如下的錯(cuò)誤:
super( B, self ).__init__( self )
TypeError: __init__() takes exactly 1 argument (2 given)
以上只是一點(diǎn)點(diǎn)本人的心得筆記,呵呵。
復(fù)制代碼 代碼如下:import threading, time
class myThread( threading.Thread ):
def __init__( self, threadname = "" ):
#threading.Thread.__init__( self, name = threadname )
super( myThread, self ).__init__( name = threadname )
def run( self ):
print "starting====", self.name, time.ctime()
time.sleep( 5 )
print "end====", self.name, time.ctime(),
m = myThread( "m" )
n = myThread( "n" )
m.start()
n.start()
輸出的結(jié)果:
starting==== m Mon Aug 08 21:55:41 2011
starting==== n Mon Aug 08 21:55:41 2011
如果一個(gè)進(jìn)程的主線程運(yùn)行完畢而子線程還在執(zhí)行的話,那么進(jìn)程就不會(huì)退出,直到所有子線程結(jié)束為止。比如下面的例子:
復(fù)制代碼 代碼如下:import threading, time
class myThread( threading.Thread ):
def __init__( self, threadname = "" ):
#threading.Thread.__init__( self, name = threadname )
super( myThread, self ).__init__( name = threadname )
def run( self ):
print "starting====", self.name, time.ctime()
time.sleep( 5 )
print "end====", self.name, time.ctime(),
m = myThread( "m" )
m.start()
print "main end"
輸出的結(jié)果為:
starting==== m Mon Aug 08 22:01:06 2011
main end
end==== m Mon Aug 08 22:01:11 2011
也就是主進(jìn)程結(jié)束之后,子進(jìn)程還沒有結(jié)束
如果我們想在主進(jìn)程結(jié)束的時(shí)候,子進(jìn)程也結(jié)束的話,我們就應(yīng)該使用setDaemon()函數(shù)。
實(shí)例如下:
復(fù)制代碼 代碼如下:import threading, time
class myThread( threading.Thread ):
def __init__( self, threadname = "" ):
#threading.Thread.__init__( self, name = threadname )
super( myThread, self ).__init__( name = threadname )
def run( self ):
print "starting====", self.name, time.ctime()
time.sleep( 5 )
print "end====", self.name, time.ctime(),
m = myThread( "m" )
m.setDaemon( True )
m.start()
print "main end"
輸出的結(jié)果為:starting====main end m Mon Aug 08 22:02:58 2011
可以看出,并沒有打印出子進(jìn)程m結(jié)束的時(shí)候本應(yīng)該打印的“end===…”
簡單的線程同步
個(gè)執(zhí)行線程經(jīng)常要共享數(shù)據(jù),如果僅僅讀取共享數(shù)據(jù)還好,但是如果多個(gè)線程要修改共享數(shù)據(jù)的話就可能出現(xiàn)無法預(yù)料的結(jié)果。
假如兩個(gè)線程對(duì)象t1和t2都要對(duì)數(shù)值num=0進(jìn)行增1運(yùn)算,那么t1和t2都各對(duì)num修改10次的話,那么num最終的結(jié)果應(yīng)該為20。但是如果當(dāng)t1取得num的值時(shí)(假如此時(shí)num為0),系統(tǒng)把t1調(diào)度為“sleeping”狀態(tài),而此時(shí)t2轉(zhuǎn)換為“running”狀態(tài),此時(shí)t2獲得的num的值也為0,然后他把num+1的值1賦給num。系統(tǒng)又把t2轉(zhuǎn)化為“sleeping”狀態(tài),t1為“running”狀態(tài),由于t1已經(jīng)得到num值為0,所以他也把num+1的值賦給了num為1。本來是2次增1運(yùn)行,結(jié)果卻是num只增了1次。類似這樣的情況在多線程同時(shí)執(zhí)行的時(shí)候是有可能發(fā)生的。所以為了防止這類情況的出現(xiàn)就要使用線程同步機(jī)制。
最簡單的同步機(jī)制就是“鎖”
鎖對(duì)象用threading.RLock類創(chuàng)建
復(fù)制代碼 代碼如下:mylock = threading.RLock()
如何使用鎖來同步線程呢?線程可以使用鎖的acquire() (獲得)方法,這樣鎖就進(jìn)入“l(fā)ocked”狀態(tài)。每次只有一個(gè)線程可以獲得鎖。如果當(dāng)另一個(gè)線程試圖獲得這個(gè)鎖的時(shí)候,就會(huì)被系統(tǒng)變?yōu)椤癰locked”狀態(tài),直到那個(gè)擁有鎖的線程調(diào)用鎖的release() (釋放)方法,這樣鎖就會(huì)進(jìn)入“unlocked”狀態(tài)?!癰locked”狀態(tài)的線程就會(huì)收到一個(gè)通知,并有權(quán)利獲得鎖。如果多個(gè)線程處于“blocked”狀態(tài),所有線程都會(huì)先解除“blocked”狀態(tài),然后系統(tǒng)選擇一個(gè)線程來獲得鎖,其他的線程繼續(xù)沉默(“blocked”)。
復(fù)制代碼 代碼如下:import threading
mylock = threading.RLock()
class mythread(threading.Thread)
...
def run(self ...):
... #此處 不可以 放置修改共享數(shù)據(jù)的代碼
mylock.acquire()
... #此處 可以 放置修改共享數(shù)據(jù)的代碼
mylock.release()
... #此處 不可以 放置修改共享數(shù)據(jù)的代碼
我們把修改共享數(shù)據(jù)的代碼稱為“臨界區(qū)”,必須將所有“臨界區(qū)”都封閉在同一鎖對(duì)象的acquire()和release()方法調(diào)用之間。
鎖只能提供最基本的同步級(jí)別。有時(shí)需要更復(fù)雜的線程同步,例如只在發(fā)生某些事件時(shí)才訪問一個(gè)臨界區(qū)(例如當(dāng)某個(gè)數(shù)值改變時(shí))。這就要使用“條件變量”。
條件變量用threading.Condition類創(chuàng)建
復(fù)制代碼 代碼如下:mycondition = threading.Condition()
條件變量是如何工作的呢?首先一個(gè)線程成功獲得一個(gè)條件變量后,調(diào)用此條件變量的wait()方法會(huì)導(dǎo)致這個(gè)線程釋放這個(gè)鎖,并進(jìn)入“blocked”狀態(tài),直到另一個(gè)線程調(diào)用同一個(gè)條件變量的notify()方法來喚醒那個(gè)進(jìn)入“blocked”狀態(tài)的線程。如果調(diào)用這個(gè)條件變量的notifyAll()方法的話就會(huì)喚醒所有的在等待的線程。
如果程序或者線程永遠(yuǎn)處于“blocked”狀態(tài)的話,就會(huì)發(fā)生死鎖。所以如果使用了鎖、條件變量等同步機(jī)制的話,一定要注意仔細(xì)檢查,防止死鎖情況的發(fā)生。對(duì)于可能產(chǎn)生異常的臨界區(qū)要使用異常處理機(jī)制中的finally子句來保證釋放鎖。等待一個(gè)條件變量的線程必須用notify()方法顯式的喚醒,否則就永遠(yuǎn)沉默。保證每一個(gè)wait()方法調(diào)用都有一個(gè)相對(duì)應(yīng)的notify()調(diào)用,當(dāng)然也可以調(diào)用notifyAll()方法以防萬一。
同步隊(duì)列
我們經(jīng)常會(huì)采用生產(chǎn)者/消費(fèi)者關(guān)系的兩個(gè)線程來處理一個(gè)共享緩沖區(qū)的數(shù)據(jù)。例如一個(gè)生產(chǎn)者線程接受用戶數(shù)據(jù)放入一個(gè)共享緩沖區(qū)里,等待一個(gè)消費(fèi)者線程對(duì)數(shù)據(jù)取出處理。但是如果緩沖區(qū)的太小而生產(chǎn)者和消費(fèi)者兩個(gè)異步線程的速度不同時(shí),容易出現(xiàn)一個(gè)線程等待另一個(gè)情況。為了盡可能的縮短共享資源并以相同速度工作的各線程的等待時(shí)間,我們可以使用一個(gè)“隊(duì)列”來提供額外的緩沖區(qū)。
創(chuàng)建一個(gè)“隊(duì)列”對(duì)象,可以使用如下代碼:
復(fù)制代碼 代碼如下:import Queue
myqueue = Queue.Queue(maxsize = 10)
Queue.Queue類即是一個(gè)隊(duì)列的同步實(shí)現(xiàn)。隊(duì)列長度可為無限或者有限??赏ㄟ^Queue的構(gòu)造函數(shù)的可選參數(shù)maxsize來設(shè)定隊(duì)列長度。如果maxsize小于1就表示隊(duì)列長度無限。
將一個(gè)值放入隊(duì)列中:
myqueue.put(10)
調(diào)用隊(duì)列對(duì)象的put()方法在隊(duì)尾插入一個(gè)項(xiàng)目。put()有兩個(gè)參數(shù),第一個(gè)item為必需的,為插入項(xiàng)目的值;第二個(gè)block為可選參數(shù),默認(rèn)為1。如果隊(duì)列當(dāng)前為空且block為1,put()方法就使調(diào)用線程暫停,直到空出一個(gè)數(shù)據(jù)單元。如果block為0,put方法將引發(fā)Full異常。
將一個(gè)值從隊(duì)列中取出:
myqueue.get()
調(diào)用隊(duì)列對(duì)象的get()方法從隊(duì)頭刪除并返回一個(gè)項(xiàng)目。可選參數(shù)為block,默認(rèn)為1。如果隊(duì)列為空且block為1,get()就使調(diào)用線程暫停,直至有項(xiàng)目可用。如果block為0,隊(duì)列將引發(fā)Empty異常。
我們用一個(gè)例子來展示如何使用Queue:
復(fù)制代碼 代碼如下:# queue_example.py
from Queue import Queue
import threading
import random
import time
# Producer thread
class Producer( threading.Thread ):
def __init__( self, threadname, queue ):
threading.Thread.__init__( self, name = threadname )
self.sharedata = queue
def run( self ):
for i in range( 20 ):
print self.getName(), 'adding', i, 'to queue'
self.sharedata.put( i )
time.sleep( random.randrange( 10 ) / 10.0 )
print self.getName(), 'Finished'
# Consumer thread
class Consumer( threading.Thread ):
def __init__( self, threadname, queue ):
threading.Thread.__init__( self, name = threadname )
self.sharedata = queue
def run( self ):
for i in range( 20 ):
print self.getName(), 'got a value:', self.sharedata.get()
time.sleep( random.randrange( 10 ) / 10.0 )
print self.getName(), 'Finished'
# Main thread
def main():
queue = Queue()
producer = Producer( 'Producer', queue )
consumer = Consumer( 'Consumer', queue )
print 'Starting threads ...'
producer.start()
consumer.start()
producer.join()
consumer.join()
print 'All threads have terminated.'
if __name__ == '__main__':
main()
程序輸出的結(jié)果為:
Starting threads ...
Producer adding 0 to queue
Consumer got a value: 0
Producer Finished
Producer adding 1 to queue
Producer Finished
Producer adding 2 to queue
Consumer Finished
Consumer got a value: 1
Consumer Finished
Consumer got a value: 2
Consumer Finished
Consumer got a value: Producer Finished
Producer adding 3 to queue
3
Consumer Finished
Consumer got a value: Producer Finished
Producer adding 4 to queue
4
ConsumerProducer Finished
ConsumerFinished
got a value:Producer adding 5 to queue
5
Consumer Finished
Consumer got a value: Producer Finished
Producer adding 6 to queue
Producer Finished
Producer adding 7 to queue
6
Consumer Finished
Consumer got a value: 7
Producer Finished
Producer adding 8 to queue
Producer Finished
Producer adding 9 to queue
Consumer Finished
Consumer got a value: 8
ConsumerProducer FinishedFinished
ConsumerProducer got a value:adding 109
to queue
Producer Finished
Producer adding 11 to queue
Producer Finished
Producer adding 12 to queue
ConsumerProducer FinishedFinished
ConsumerProducer got a value:adding 1310
to queue
Producer Finished
Producer adding 14 to queue
Consumer Finished
Consumer got a value: 11
Producer Finished
Producer adding 15 to queue
Producer Finished
Producer adding 16 to queue
Producer Finished
Producer adding 17 to queue
Producer Finished
Producer adding 18 to queue
Consumer Finished
Consumer got a value: 12
Producer Finished
Producer adding 19 to queue
Producer Finished
Consumer Finished
Consumer got a value: 13
Consumer Finished
Consumer got a value: 14
Consumer Finished
Consumer got a value: 15
Consumer Finished
Consumer got a value: 16
Consumer Finished
Consumer got a value: 17
Consumer Finished
Consumer got a value: 18
Consumer Finished
Consumer got a value: 19
Consumer Finished
All threads have terminated.
希望本文所述對(duì)大家的Python程序設(shè)計(jì)有所幫助。
更多信息請(qǐng)查看IT技術(shù)專欄