一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Python線程編程

 alai 2009-04-14

Python線程編程

我們在做軟件開發(fā)的時候很多要用到多線程技術(shù)。例如如果做一個下載軟件象flashget就要用到、象在線視頻工具realplayer也要用到因為要同時下載media stream還要播放。其實例子是很多的。

線程相對進程來說是“輕量級”的,操作系統(tǒng)用較少的資源創(chuàng)建和管理線程。程序中的線程在相同的內(nèi)存空間中執(zhí)行,并共享許多相同的資源。

在python中如何創(chuàng)建一個線程對象

如果你要創(chuàng)建一個線程對象,很簡單,只要你的類繼承threading.Thread,然后在__init__里首先調(diào)用threading.Thread的__init__方法即可

import threading
class mythread(threading.Thread):
def __init__(self, threadname):
threading.Thread.__init__(self, name = threadname)
....

這才僅僅是個空線程,我可不是要他拉空車的,他可得給我干點實在活。很簡單,重寫類的run()方法即可,把你要在線程執(zhí)行時做的事情都放到里面

import threading
import time
class mythread(threading.Thread):
def __init__(...):
....
def run(self):
for i in range(10):
print self.getName, i
time.sleep(1)

以上代碼我們讓這個線程在執(zhí)行之后每隔1秒輸出一次信息到屏幕,10次后結(jié)束

getName()是threading.Thread類的一個方法,用來獲得這個線程對象的name。還有一個方法setName()當(dāng)然就是來設(shè)置這個線程對象的name的了。

如果要創(chuàng)建一個線程,首先就要先創(chuàng)建一個線程對象

mythread1 = mythread('mythread 1')

一個線程對象被創(chuàng)建后,他就處于“born”(誕生狀態(tài))

如何讓這個線程對象開始運行呢?只要調(diào)用線程對象的start()方法即可

mythread1.start()

現(xiàn)在線程就處于“ready”狀態(tài)或者也稱為“runnable”狀態(tài)。

奇怪嗎?不是已經(jīng)start了嗎?為什么不稱為“running”狀態(tài)呢?其實是有原因的。因為我們的計算機一般是不具有真正并行處理能力的。我們所謂的多線程只是把時間分成片段,然后隔一個時間段就讓一個線程執(zhí)行一下,然后進入“sleeping ”狀態(tài),然后喚醒另一個在“sleeping”的線程,如此循環(huán)runnable->sleeping->runnable... ,只是因為計算機執(zhí)行速度很快,而時間片段間隔很小,我們感受不到,以為是同時進行的。所以說一個線程在start了之后只是處在了可以運行的狀態(tài),他什么時候運行還是由系統(tǒng)來進行調(diào)度的。

那一個線程什么時候會“dead”呢?一般來說當(dāng)線程對象的run方法執(zhí)行結(jié)束或者在執(zhí)行中拋出異常的話,那么這個線程就會結(jié)束了。系統(tǒng)會自動對“dead”狀態(tài)線程進行清理。

如果一個線程t1在執(zhí)行的過程中需要等待另一個線程t2執(zhí)行結(jié)束后才能運行的話那就可以在t1在調(diào)用t2的join()方法

....
def t1(...):
...
t2.join()
...

這樣t1在執(zhí)行到t2.join()語句后就會等待t2結(jié)束后才會繼續(xù)運行。

但是假如t1是個死循環(huán)的話那么等待就沒有意義了,那怎么辦呢?可以在調(diào)用t2的join()方法的時候給一個浮點數(shù)做超時參數(shù),這樣這個線程就不會等到花兒也謝了了。我等你10s,你不回來我還不允許我改嫁啊?:)

def t1(...):
...
t2.join(10)
...

如果一個進程的主線程運行完畢而子線程還在執(zhí)行的話,那么進程就不會退出,直到所有子線程結(jié)束為止,如何讓主線程結(jié)束的時候其他子線程也乖乖的跟老大撤退呢?那就要把那些不聽話的人設(shè)置為聽話的小弟,使用線程對象的setDaemon()方法,參數(shù)為bool型。True的話就代表你要聽話,我老大(主線程)扯呼,你也要跟著撤,不能拖后腿。如果是False的話就不用那么聽話了,老大允許你們將在外軍命有所不受的。需要注意的是setDaemon()方法必須在線程對象沒有調(diào)用start()方法之前調(diào)用,否則沒效果。

t1 = mythread('t1')
print t1.getName(),t1.isDaemon()
t1.setDaemon(True)
print t1.getName(),t1.isDaemon()
t1.start()
print 'main thread exit'

當(dāng)執(zhí)行到 print 'main thread exit' 后,主線程就退出了,當(dāng)然t1這個線程也跟著結(jié)束了。但是如果不使用t1線程對象的setDaemon()方法的話,即便主線程結(jié)束了,還要等待t1線程自己結(jié)束才能退出進程。isDaemon()是用來獲得一個線程對象的Daemonflag狀態(tài)的。

如何來獲得與線程有關(guān)的信息呢?

獲得當(dāng)前正在運行的線程的引用

running = threading.currentThread()

獲得當(dāng)前所有活動對象(即run方法開始但是未終止的任何線程)的一個列表

threadlist = threading.enumerate()

獲得這個列表的長度

threadcount = threading.activeCount()

查看一個線程對象的狀態(tài)調(diào)用這個線程對象的isAlive()方法,返回1代表處于“runnable”狀態(tài)且沒有“dead”

threadflag = threading.isAlive()


Python線程編程(二)簡單的線程同步



    多個執(zhí)行線程經(jīng)常要共享數(shù)據(jù),如果僅僅讀取共享數(shù)據(jù)還好,但是如果多個線程要修改共享數(shù)據(jù)的話就可能出現(xiàn)無法預(yù)料的結(jié)果。

    假如兩個線程對象t1和t2都要對數(shù)值num=0進行增1運算,那么t1和t2都各對num修改10次的話,那么num最終的結(jié)果應(yīng)該為20。但是如果當(dāng)t1取得num的值時(假如此時num為0),系統(tǒng)把t1調(diào)度為“sleeping”狀態(tài),而此時t2轉(zhuǎn)換為“running”狀態(tài),此時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運行,結(jié)果卻是num只增了1次。類似這樣的情況在多線程同時執(zhí)行的時候是有可能發(fā)生的。所以為了防止這類情況的出現(xiàn)就要使用線程同步機制。

    最簡單的同步機制就是“鎖”

    鎖對象用threading.RLock類創(chuàng)建

    mylock = threading.RLock()

    如何使用鎖來同步線程呢?線程可以使用鎖的acquire() (獲得)方法,這樣鎖就進入“locked”狀態(tài)。每次只有一個線程可以獲得鎖。如果當(dāng)另一個線程試圖獲得這個鎖的時候,就會被系統(tǒng)變?yōu)?#8220;blocked”狀態(tài),直到那個擁有鎖的線程調(diào)用鎖的release() (釋放)方法,這樣鎖就會進入“unlocked”狀態(tài)。“blocked”狀態(tài)的線程就會收到一個通知,并有權(quán)利獲得鎖。如果多個線程處于“blocked”狀態(tài),所有線程都會先解除“blocked”狀態(tài),然后系統(tǒng)選擇一個線程來獲得鎖,其他的線程繼續(xù)沉默(“blocked”)。

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ū)”都封閉在同一鎖對象的acquire()和release()方法調(diào)用之間。

    鎖只能提供最基本的同步級別。有時需要更復(fù)雜的線程同步,例如只在發(fā)生某些事件時才訪問一個臨界區(qū)(例如當(dāng)某個數(shù)值改變時)。這就要使用“條件變量”。

    條件變量用threading.Condition類創(chuàng)建

    mycondition = threading.Condition()

    條件變量是如何工作的呢?首先一個線程成功獲得一個條件變量后,調(diào)用此條件變量的wait()方法會導(dǎo)致這個線程釋放這個鎖,并進入“blocked”狀態(tài),直到另一個線程調(diào)用同一個條件變量的notify()方法來喚醒那個進入“blocked”狀態(tài)的線程。如果調(diào)用這個條件變量的notifyAll()方法的話就會喚醒所有的在等待的線程。

    如果程序或者線程永遠處于“blocked”狀態(tài)的話,就會發(fā)生死鎖。所以如果使用了鎖、條件變量等同步機制的話,一定要注意仔細檢查,防止死鎖情況的發(fā)生。對于可能產(chǎn)生異常的臨界區(qū)要使用異常處理機制中的finally子句來保證釋放鎖。等待一個條件變量的線程必須用notify()方法顯式的喚醒,否則就永遠沉默。保證每一個wait()方法調(diào)用都有一個相對應(yīng)的notify()調(diào)用,當(dāng)然也可以調(diào)用notifyAll()方法以防萬一。


Python線程編程(三)同步隊列


我們經(jīng)常會采用生產(chǎn)者/消費者關(guān)系的兩個線程來處理一個共享緩沖區(qū)的數(shù)據(jù)。例如一個生產(chǎn)者線程接受用戶數(shù)據(jù)放入一個共享緩沖區(qū)里,等待一個 消費者線程對數(shù)據(jù)取出處理。但是如果緩沖區(qū)的太小而生產(chǎn)者和消費者兩個異步線程的速度不同時,容易出現(xiàn)一個線程等待另一個情況。為了盡可能的縮短共享資源 并以相同速度工作的各線程的等待時間,我們可以使用一個“隊列”來提供額外的緩沖區(qū)。

創(chuàng)建一個“隊列”對象

import Queue
myqueue = Queue.Queue(maxsize = 10)

Queue.Queue類即是一個隊列的同步實現(xiàn)。隊列長度可為無限或者有限。可通過Queue的構(gòu)造函數(shù)的可選參數(shù)maxsize來設(shè)定隊列長度。如果maxsize小于1就表示隊列長度無限。

將一個值放入隊列中

myqueue.put(10)

調(diào)用隊列對象的put()方法在隊尾插入一個項目。put()有兩個參數(shù),第一個item為必需的,為插入項目的值;第二個block為可選參數(shù),默認為1。如果隊列當(dāng)前為空且block為1,put()方法就使調(diào)用線程暫停,直到空出一個數(shù)據(jù)單元。如果block為0,put方法將引發(fā)Full異常。

將一個值從隊列中取出

myqueue.get()

調(diào)用隊列對象的get()方法從隊頭刪除并返回一個項目??蛇x參數(shù)為block,默認為1。如果隊列為空且block為1,get()就使調(diào)用線程暫停,直至有項目可用。如果block為0,隊列將引發(fā)Empty異常。

我們用一個例子來展示如何使用Queue

# 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()

示例代碼中實現(xiàn)了兩個類:生產(chǎn)者類Producer和消費者類Consumer。前者在一個隨機的時間內(nèi)放入一個值到隊列queue中然后顯示出來,后者在一定隨機的時間內(nèi)從隊列queue中取出一個值并顯示出來。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日本婷婷色大香蕉视频在线观看| 国产又色又粗又黄又爽| 国产视频福利一区二区| 国产精品欧美激情在线| 午夜精品久久久99热连载| 国产亚洲二区精品美女久久| 色偷偷偷拍视频在线观看| 亚洲成人免费天堂诱惑| 91超精品碰国产在线观看| 中字幕一区二区三区久久蜜桃| 91国内视频一区二区三区| 国产一级片内射视频免费播放| 九九热这里只有精品哦| 亚洲欧美日韩色图七区| 青青操视频在线观看国产| 91精品日本在线视频| 国产一级特黄在线观看| 在线一区二区免费的视频| 精品久久久一区二区三| 成在线人免费视频一区二区| 国产av天堂一区二区三区粉嫩| 五月激情综合在线视频| 国产精品夜色一区二区三区不卡| 亚洲国产另类久久精品| 日本午夜免费福利视频| 91欧美视频在线观看免费| 日本女优一色一伦一区二区三区| av在线免费播放一区二区| 久草热视频这里只有精品| 黑鬼糟蹋少妇资源在线观看| 激情五月综五月综合网| 久久人妻人人澡人人妻| 日韩欧美好看的剧情片免费| 黑人粗大一区二区三区| 99热九九热这里只有精品| 日本大学生精油按摩在线观看| 欧美日韩精品综合在线| 国语对白刺激高潮在线视频| 国产精品香蕉在线的人| 国产精品免费精品一区二区| 亚洲高清欧美中文字幕|