python中的多線程實(shí)例教程
本文以實(shí)例形式較為詳細(xì)的講述了Python中多線程的用法,在Python程序設(shè)計(jì)中有著比較廣泛的應(yīng)用。分享給大家供大家參考之用。具體分析如下:
python中關(guān)于多線程的操作可以使用thread和threading模塊來(lái)實(shí)現(xiàn),其中thread模塊在Py3中已經(jīng)改名為_thread,不再推薦使用。而threading模塊是在thread之上進(jìn)行了封裝,也是推薦使用的多線程模塊,本文主要基于threading模塊進(jìn)行介紹。在某些版本中thread模塊可能不存在,要使用dump_threading來(lái)代替threading模塊。
一、線程創(chuàng)建
threading模塊中每個(gè)線程都是一個(gè)Thread對(duì)象,創(chuàng)建一個(gè)線程有兩種方式,一種是將函數(shù)傳遞到Thread對(duì)象中執(zhí)行,另一種是從Thread繼承,然后重寫run方法(是不是跟Java很像)。
下面使用這兩種方法分別創(chuàng)建一個(gè)線程并同時(shí)執(zhí)行
import random, threading def threadFunction(): for i in range(10): print 'ThreadFuction - %d'%i time.sleep(random.randrange(0,2)) class ThreadClass(threading.Thread): def __init__(self): threading.Thread.__init__(self); def run(self): for i in range(10): print 'ThreadClass - %d'%i time.sleep(random.randrange(0,2)) if __name__ == '__main__': tFunc = threading.Thread(target = threadFunction); tCls = ThreadClass() tFunc.start() tCls.start()
執(zhí)行結(jié)果如下,可以看到兩個(gè)線程在交替打印。至于空行和一行多個(gè)輸出,是因?yàn)镻y的print并不是線程安全的,在當(dāng)前線程的print打印了部分內(nèi)容后,準(zhǔn)備打印換行之前,被別的線程中的print搶先,在換行之前打印了其它的內(nèi)容。
ThreadFuction - 0 ThreadFuction - 1 ThreadFuction - 2 ThreadClass - 0 ThreadFuction - 3 ThreadClass - 1 ThreadFuction - 4 ThreadClass - 2 ThreadClass - 3 ThreadClass - 4ThreadFuction - 5 ThreadClass - 5 ThreadClass - 6 ThreadClass - 7 ThreadClass - 8 ThreadFuction - 6ThreadClass - 9 ThreadFuction - 7 ThreadFuction - 8 ThreadFuction - 9
Thread類的構(gòu)造函數(shù)定義如下
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})
group: 留作ThreadGroup擴(kuò)展使用,一般沒什么用
target:新線程的任務(wù)函數(shù)名
name: 線程名,一般也沒什么用
args: tuple參數(shù)
kwargs:dictionary參數(shù)
Thread類的成員變量和函數(shù)如下
start() 啟動(dòng)一個(gè)線程
run() 線程執(zhí)行體,也是一般要重寫的內(nèi)容
join([timeout]) 等待線程結(jié)束
name 線程名
ident 線程ID
daemon 是否守護(hù)線程
isAlive()、is_alive() 線程是否存活
getName()、setName() Name的get&set方法
isDaemon()、setDaemon() daemon的get&set方法
這里的守護(hù)線程與Linux中的守護(hù)進(jìn)程并不是一個(gè)概念。這里是指當(dāng)所有守護(hù)線程退出后主程序才會(huì)退出,否則即使線程任務(wù)沒有結(jié)束,只要不是守護(hù)線程,都會(huì)跟著主程序一起退出。而Linux中的守護(hù)進(jìn)程定義正好相反,守護(hù)進(jìn)程已經(jīng)脫離父進(jìn)程,不會(huì)隨著父進(jìn)程的結(jié)束而退出。
二、線程同步
線程同步是多線程中的一個(gè)核心問(wèn)題,threading模塊對(duì)線程同步有著良好的支持、包括線程特定數(shù)據(jù)、信號(hào)量、互斥鎖、條件變量等。
1.線程特定數(shù)據(jù)
簡(jiǎn)而言之,線程特定數(shù)據(jù)就是線程獨(dú)自持有的全局變量,相互之間的修改不會(huì)造成影響。
threading模塊中使用local()方法生成一個(gè)線程獨(dú)立對(duì)象,舉例如下,其中sleep(1)是為了保證讓子線程先運(yùn)行完再運(yùn)行接下來(lái)的語(yǔ)句。
data = threading.local() def threadFunction(): global data data.x = 3 print threading.currentThread(), data.x if __name__ == '__main__': data.x = 1 tFunc = threading.Thread(target = threadFunction).start(); time.sleep(1) print threading.current_thread(), data.x
<Thread(Thread-1, started 36208)> 3 <_MainThread(MainThread, started 35888)> 1
輸出如上,可以看到,Thread-1中對(duì)data.x的修改并沒有影響到主線程中data.x的值。
2.互斥鎖
threading中定義了兩種鎖:threading.Lock和threading.RLock。兩者的不同在于后者是可重入鎖,也就是說(shuō)在一個(gè)線程內(nèi)重復(fù)LOCK同一個(gè)鎖不會(huì)發(fā)生死鎖,這與POSIX中的PTHREAD_MUTEX_RECURSIVE也就是可遞歸鎖的概念是相同的。
關(guān)于互斥鎖的API很簡(jiǎn)單,只有三個(gè)函數(shù)————分配鎖,上鎖,解鎖。
threading.Lock() 分配一個(gè)互斥鎖
acquire([blocking=1]) 上鎖(阻塞或者非阻塞,非阻塞時(shí)相當(dāng)于try_lock,通過(guò)返回False表示已經(jīng)被其它線程鎖住。)
release() 解鎖
下面通過(guò)一個(gè)例子來(lái)說(shuō)明互斥鎖的使用。在之前的例子中,多線程print會(huì)造成混亂的輸出,這里使用一個(gè)互斥鎖,來(lái)保證每行一定只有一個(gè)輸出。
def threadFunction(arg): while True: lock.acquire() print 'ThreadFuction - %d'%arg lock.release() if __name__ == '__main__': lock = threading.Lock() threading.Thread(target = threadFunction, args=(1,)).start(); threading.Thread(target = threadFunction, args=(2,)).start();
3.條件變量
條件變量總是與互斥鎖一起使用的,threading中的條件變量默認(rèn)綁定了一個(gè)RLock,也可以在初始化條件變量的時(shí)候傳進(jìn)去一個(gè)自己定義的鎖。
可用的函數(shù)如下
threading.Condition([lock]) 分配一個(gè)條件變量 acquire(*args) 條件變量上鎖 release() 條件變量解鎖 wait([timeout]) 等待喚醒,timeout表示超時(shí) notify(n=1) 喚醒最大n個(gè)等待的線程 notifyAll()、notify_all() 喚醒所有等待的線程 下面這個(gè)例子使用條件變量來(lái)控制兩個(gè)線程交替運(yùn)行 num = 0 def threadFunction(arg): global num while num < 10: cond.acquire() while num % 2 != arg: cond.wait() print 'Thread %d - %d' %(arg, num) num += 1 cond.notify() cond.release() if __name__ == '__main__': cond = threading.Condition() threading.Thread(target = threadFunction, args=(0,)).start(); threading.Thread(target = threadFunction, args=(1,)).start();
輸出如下
Thread 0 - 0 Thread 1 - 1 Thread 0 - 2 Thread 1 - 3 Thread 0 - 4 Thread 1 - 5 Thread 0 - 6 Thread 1 - 7 Thread 0 - 8 Thread 1 - 9 Thread 0 - 10
其實(shí)上面這個(gè)程序是有問(wèn)題的,我們想打印的是0~9,但實(shí)際上10也被打印了出來(lái),原因很簡(jiǎn)單,因?yàn)閮蓚€(gè)線程交替打印,使得num在一個(gè)線程中可能加2,從而導(dǎo)致10被打印出來(lái),所以必須在打印前再次check。
相信本文所述對(duì)大家的Python程序設(shè)計(jì)有一定的借鑒價(jià)值。
相關(guān)文章
PyCharm代碼整體縮進(jìn),反向縮進(jìn)的方法
今天小編就為大家分享一篇PyCharm代碼整體縮進(jìn),反向縮進(jìn)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06python之PyAutoGui教你做個(gè)自動(dòng)腳本計(jì)算器的方法
這篇文章主要介紹了python之PyAutoGui教你做個(gè)自動(dòng)腳本計(jì)算器的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Python3.4學(xué)習(xí)筆記之常用操作符,條件分支和循環(huán)用法示例
這篇文章主要介紹了Python3.4常用操作符,條件分支和循環(huán)用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Python3.4常見的數(shù)學(xué)運(yùn)算、邏輯運(yùn)算操作符,條件分支語(yǔ)句,循環(huán)語(yǔ)句等功能與基本用法,需要的朋友可以參考下2019-03-03Python基于內(nèi)置庫(kù)pytesseract實(shí)現(xiàn)圖片驗(yàn)證碼識(shí)別功能
這篇文章主要介紹了Python基于內(nèi)置庫(kù)pytesseract實(shí)現(xiàn)圖片驗(yàn)證碼識(shí)別功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02python3庫(kù)numpy數(shù)組屬性的查看方法
下面小編就為大家分享一篇python3庫(kù)numpy數(shù)組屬性的查看方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04pytorch-RNN進(jìn)行回歸曲線預(yù)測(cè)方式
今天小編就為大家分享一篇pytorch-RNN進(jìn)行回歸曲線預(yù)測(cè)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01Python編程實(shí)現(xiàn)超炫動(dòng)態(tài)排序圖
這篇文章主要介紹了Python編程實(shí)現(xiàn)超炫動(dòng)態(tài)排序圖的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10Pytorch建模過(guò)程中的DataLoader與Dataset示例詳解
這篇文章主要介紹了Pytorch建模過(guò)程中的DataLoader與Dataset,同時(shí)PyTorch針對(duì)不同的專業(yè)領(lǐng)域,也提供有不同的模塊,例如?TorchText,?TorchVision,?TorchAudio,這些模塊中也都包含一些真實(shí)數(shù)據(jù)集示例,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-01-01