Python多線程實(shí)例教程
本文以實(shí)例形式較為詳細(xì)的講解了Python的多線程,是Python程序設(shè)計(jì)中非常重要的知識(shí)點(diǎn)。分享給大家供大家參考之用。具體方法如下:
用過(guò)Python的人都會(huì)覺(jué)得Python的多線程很類似于Java的多線程機(jī)制,但是比JAVA的多線程更靈活。在早期的Python多線程實(shí)現(xiàn)中,采用了thread模塊。例如:
from time import ctime,sleep from thread import start_new_thread def loop1(): print "enter loop1:",ctime(); sleep(3); print "leave loop1:",ctime(); def loop2(): print "enter loop2:",ctime(); sleep(5); print "leave loop2:",ctime(); def main(): print "main begin:",ctime(); start_new_thread(loop1, ()); start_new_thread(loop2,()); sleep(8); print "main end:",ctime(); if __name__=="__main__": main();
簡(jiǎn)單介紹下這個(gè)代碼塊中的函數(shù)功能,sleep是線程睡眠時(shí)間,幾乎等價(jià)于JAVA中的Thread.sleep(millionseconds)
start_new_thread是實(shí)例化一個(gè)線程并運(yùn)行的方法,方法的第一個(gè)參數(shù)接受一個(gè)線程運(yùn)行時(shí)所執(zhí)行的函數(shù)對(duì)象,第二個(gè)參數(shù)是方法執(zhí)行時(shí)所需要的參數(shù),以一個(gè)元組的形式傳入。
這大概是最早期的Python多線程實(shí)現(xiàn)了,注意代碼中的main線程里的sleep(8)。這里的睡眠時(shí)間只能比3+5大,而不能小。如果小于這個(gè)時(shí)間,那么main主線程會(huì)提前退出,導(dǎo)致無(wú)論其子線程是否是后臺(tái)線程,都將會(huì)中斷,從而拋出線程中斷異常,類似于Java的ThreadInterruptException。這個(gè)致命的影響幾乎就是這個(gè)模塊后期被拋棄的罪魁禍?zhǔn)住?/p>
當(dāng)然在早期的Python多線程中,你可以利用加鎖的機(jī)制來(lái)避免出現(xiàn)這個(gè)情況。稍微改動(dòng)下以上代碼:
import thread; from time import sleep,ctime; from random import choice #The first param means the thread number #The second param means how long it sleep #The third param means the Lock def loop(nloop,sec,lock): print "Thread ",nloop," start and will sleep ",sec; sleep(sec); print "Thread ",nloop," end ",sec; lock.release(); def main(): seconds=[4,2]; locks=[]; for i in range(len(seconds)) : lock=thread.allocate_lock(); lock.acquire(); locks.append(lock); print "main Thread begins:",ctime(); for i,lock in enumerate(locks): thread.start_new_thread(loop,(i,choice(seconds),lock)); for lock in locks : while lock.locked() : pass; print "main Thread ends:",ctime(); if __name__=="__main__" : main();
這里對(duì)Python線程運(yùn)行時(shí)加入了鎖監(jiān)控機(jī)制,介紹下紅色字體標(biāo)志的幾個(gè)方法(其實(shí)紅色字體中的lock實(shí)質(zhì)是thread.lockType實(shí)例。
從以上介紹可以看出這個(gè)Lock類非常類似于JDK5.0中的java.util.concurrent.locks.Lock。不知道Doug Lea有沒(méi)有參與這個(gè)模塊的開(kāi)發(fā),只是比JAVA中的LOCK類多了一個(gè)方法locked,用于檢測(cè)Lock對(duì)象是否還處于加鎖的狀態(tài)。
所以上一個(gè)例子的工作原理就是在啟動(dòng)線程的時(shí)候,給每個(gè)線程都加了一把鎖,直到線程運(yùn)行介紹,再釋放這個(gè)鎖。同時(shí)在Python的main線程中用一個(gè)while循環(huán)來(lái)不停的判斷每個(gè)線程鎖已釋放。這個(gè)方法雖然避免了最開(kāi)始的例子中人為的時(shí)間控制,但是還不方便,高效。
所以在較新的Python版本中,都推薦使用threading模塊。
看下threading模塊的API,有過(guò)JAVA開(kāi)發(fā)經(jīng)驗(yàn)的會(huì)發(fā)現(xiàn)它和java.lang.Thread類非常接近。這里需要說(shuō)的一點(diǎn)就是threading的run方法可以返回函數(shù)值,這點(diǎn)在用于跟蹤和判斷線程運(yùn)行正常與否非常有作用。
threading模塊支持三種方法來(lái)創(chuàng)建線程。而前兩種方式都與其Thread類有關(guān)??聪滤暮?jiǎn)要說(shuō)明:
class Thread(_Verbose) : __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None)
其中target指的是一個(gè)具體的函數(shù),或者可調(diào)用的類實(shí)例(這里指實(shí)現(xiàn)了__call__方法的類實(shí)例)
第一種方法:指定線程運(yùn)行的時(shí)候調(diào)用的函數(shù)。舉例如下:
from time import ctime,sleep import threading; from random import choice def loop(number,sec): print "Thread ",number," begins and will sleep ",sec," at ",ctime(); sleep(sec); print "Thread ",number,"ends at ",ctime(); def main(): seconds=[2,4]; threads=[]; array=range(len(seconds)); for i in array : t=threading.Thread(target=loop,args=(i,choice(seconds))); threads.append(t); print "main Thread begins at ",ctime(); for t in threads : t.start(); for t in threads : t.join(); print "main Thread ends at ",ctime(); if __name__=="__main__" : main();
這里target指向了一個(gè)具體的函數(shù)對(duì)象,而args傳入了該方法調(diào)用時(shí)所必須的參數(shù)。這里傳入了一個(gè)隨即的睡眠時(shí)間。其中thread.join表示要等待該線程終止,和java中的Thread.join(long millionseconds)作用一樣,如果不指定具體的時(shí)間的話,將會(huì)一直等待下去。
第二種方法就是指定一個(gè)可調(diào)用的類實(shí)例,實(shí)際上與前面一種非常的接近。如下所示:
from time import ctime,sleep import threading; from random import choice class ThreadFunc(object): def __init__(self,func,args,name): self.func=func; self.args=args; self.name=name; def __call__(self): self.func(*self.args); def loop(number,sec): print "Thread ",number," begins and will sleep ",sec," at ",ctime(); sleep(sec); print "Thread ",number,"ends at ",ctime(); def main(): seconds=[2,4]; threads=[]; array=range(len(seconds)); for i in array : t=threading.Thread(target=ThreadFunc(loop,(i,choice(seconds)),loop.__name__)); threads.append(t); print "main Thread begins at ",ctime(); for t in threads : t.start(); for t in threads : t.join(); print "main Thread ends at ",ctime(); if __name__=="__main__" : main();
這里只是將target指向從一個(gè)函數(shù)對(duì)象變成了一個(gè)可調(diào)用的類實(shí)例。
重點(diǎn)推薦下第三種方式,用繼承threading.Thread的方式來(lái)實(shí)現(xiàn)線程,有過(guò)Java多線程應(yīng)用的朋友一定會(huì)對(duì)下面的例子非常熟悉。
from time import ctime,sleep import threading; from random import choice class MyThread(threading.Thread): def __init__(self,func,args,name): super(MyThread,self).__init__(); self.func=func; self.args=args; self.name=name; def run(self): self.result=self.func(*self.args); def getResult(self): return self.result; def loop(number,sec): print "Thread ",number," begins and will sleep ",sec," at ",ctime(); sleep(sec); print "Thread ",number,"ends at ",ctime(); def main(): seconds=[2,4]; threads=[]; array=range(len(seconds)); for i in array : t=MyThread(loop,(i,choice(seconds)),loop.__name__); threads.append(t); print "main Thread begins at ",ctime(); for t in threads : t.start(); for t in threads : t.join(); print "main Thread ends at ",ctime(); if __name__=="__main__" : main();
從上面可以看出MyThread繼承了threading.Thread類,并在初始化方法中執(zhí)行了必要的參數(shù)賦值。值得注意的是在Java類的繼承中,如果不顯示的指定調(diào)用父類的構(gòu)造方法,那么默認(rèn)將調(diào)用父類的無(wú)參構(gòu)造方法。而在Python中,就不會(huì)主動(dòng)去調(diào)用。所以這里需要顯示的調(diào)用父類的初始化方法。
希望本文所述對(duì)大家的Python程序設(shè)計(jì)有所幫助。
相關(guān)文章
Windows下的Python 3.6.1的下載與安裝圖文詳解(適合32位和64位)
這篇文章主要介紹了Windows下的Python 3.6.1的下載與安裝圖文詳解(適合32位和64位),需要的朋友可以參考下2018-02-02python 實(shí)現(xiàn)將txt文件多行合并為一行并將中間的空格去掉方法
今天小編就為大家分享一篇python 實(shí)現(xiàn)將txt文件多行合并為一行并將中間的空格去掉方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12利用Python爬蟲(chóng)爬取金融期貨數(shù)據(jù)的案例分析
從技術(shù)角度來(lái)看,經(jīng)過(guò)一步步解析,任務(wù)是簡(jiǎn)單的,入門requests爬蟲(chóng)及入門pandas數(shù)據(jù)分析就可以完成,本文重點(diǎn)給大家介紹Python爬蟲(chóng)爬取金融期貨數(shù)據(jù)的案例分析,感興趣的朋友一起看看吧2022-06-06python實(shí)現(xiàn)去除下載電影和電視劇文件名中的多余字符的方法
這篇文章主要介紹了python實(shí)現(xiàn)去除下載電影和電視劇文件名中的多余字符的方法,可以批量修改視頻文件名稱,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-09-09python動(dòng)態(tài)性強(qiáng)類型用法實(shí)例
這篇文章主要介紹了python動(dòng)態(tài)性強(qiáng)類型用法,對(duì)比C#實(shí)例分析了python動(dòng)態(tài)性強(qiáng)類型的使用方法,需要的朋友可以參考下2015-05-05如何利用Python分析出微信朋友男女統(tǒng)計(jì)圖
這篇文章主要給大家介紹了關(guān)于如何利用Python分析出微信朋友男女統(tǒng)計(jì)圖的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧2019-01-01Flask如何接收前端ajax傳來(lái)的表單(包含文件)
這篇文章主要介紹了Flask如何接收前端ajax傳來(lái)的表單(包含文件),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Django 忘記管理員或忘記管理員密碼 重設(shè)登錄密碼的方法
今天小編就為大家分享一篇Django 忘記管理員或忘記管理員密碼 重設(shè)登錄密碼的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05