摘要:介紹今天花了近乎一天的時(shí)間研究關(guān)于多線程的問題,查看了大量源碼自己也實(shí)踐了一個(gè)生產(chǎn)消費(fèi)者模型,所以把一天的收獲總結(jié)一下。提供了兩個(gè)模塊和來支持的多線程操作。使用來阻塞線程。
介紹
今天花了近乎一天的時(shí)間研究python關(guān)于多線程的問題,查看了大量源碼 自己也實(shí)踐了一個(gè)生產(chǎn)消費(fèi)者模型,所以把一天的收獲總結(jié)一下。
由于GIL(Global Interpreter Lock)鎖的關(guān)系,純的python代碼處理一般邏輯的確無法活動(dòng)性能上的極大提升,但是在處理需要等待外部資源返回或多用戶的應(yīng)用程序中,多線程仍然可以作為一個(gè)比較好的工具來進(jìn)行使用。
threading and threadpython提供了兩個(gè)模塊thread和threading 來支持python的多線程操作。通俗的講一般現(xiàn)在我們只使用threading模塊來編程了,thread模塊定義了很多原始行為,更接近底層,而threading模塊抽象了thread模塊可用性更好,同時(shí)提供更多特性。
現(xiàn)在創(chuàng)建線程的通用方法一般是創(chuàng)建一個(gè)類并且繼承threading.Thread,然后重寫其中的__init__和run()方法。 更多詳情可以參考threading模塊代碼內(nèi)注釋以及代碼。下面直接看個(gè)例子。
import time import threading class Test(threading.Thread): def __init__(self, name, delay): super(Test, self).__init__() self.name = name self.delay = delay def run(self): print "%s delay for %s seconds" % (self.name, self.delay) time.sleep(self.delay) c = 0 while True: print "This is thread %s on line %s" % (self.name, c) c += 1 if c == 3: print "End of thread %s" % self.name break t1 = Test("Thread1", 5) t2 = Test("Thread2", 5) t1.start() print "Wait t1 to end" t1.join() t2.start() t2.join() print "End of main"
注意一下這一句 :
super(Test, self).__init__()
這是按照模塊要求,必須初始化父類的__init__函數(shù) 所以使用了super()
其他并沒有多少值得注意的地方,
創(chuàng)建線程方便的實(shí)例化自己寫的繼承threading.Thread的類 然后傳入對(duì)應(yīng)的參數(shù)。
最后使用xxx.start()來運(yùn)行線程。 使用xxx.join()來阻塞線程。
特別注意的是。繼承自Threading類的子類還有一個(gè)daemon參數(shù),如果這個(gè)參數(shù)適用setDaemon()方法置為True之后,主線程將不會(huì)等待子線程都結(jié)束之后才結(jié)束,而是自己運(yùn)行完之后就結(jié)束,這種方式相當(dāng)粗暴。 如果將daemon參數(shù)設(shè)置為False的話,主線成將會(huì)等待所有子線程結(jié)束之后再結(jié)束。daemon屬性可以通過使用isDaemon()方法獲取一個(gè)boolean值。
互斥與同步更進(jìn)一步的,我必須介紹一下線程之間的同步和互斥問題。下面引用《計(jì)算機(jī)操作系統(tǒng)》中的介紹。
生產(chǎn)消費(fèi)者的經(jīng)典例子當(dāng)線程并發(fā)執(zhí)行時(shí),由于資源共享和線程協(xié)作,使用線程之間會(huì)存在以下兩種制約關(guān)系。
(1)間接相互制約。一個(gè)系統(tǒng)中的多個(gè)線程必然要共享某種系統(tǒng)資源,如共享CPU,共享I/O設(shè)備,所謂間接相互制約即源于這種資源共享,打印機(jī)就是最好的例子,線程A在使用打印機(jī)時(shí),其它線程都要等待。
(2)直接相互制約。這種制約主要是因?yàn)榫€程之間的合作,如有線程A將計(jì)算結(jié)果提供給線程B作進(jìn)一步處理,那么線程B在線程A將數(shù)據(jù)送達(dá)之前都將處于阻塞狀態(tài)。
間接相互制約可以稱為互斥,直接相互制約可以稱為同步,對(duì)于互斥可以這樣理解,線程A和線程B互斥訪問某個(gè)資源則它們之間就會(huì)產(chǎn)個(gè)順序問題——要么線程A等待線程B操作完畢,要么線程B等待線程操作完畢,這其實(shí)就是線程的同步了。因此同步包括互斥,互斥其實(shí)是一種特殊的同步。
在一段時(shí)間內(nèi)只允許一個(gè)線程訪問的資源就稱為臨界資源或獨(dú)占資源,計(jì)算機(jī)中大多數(shù)物理設(shè)備,進(jìn)程中的共享變量等待都是臨界資源,它們要求被互斥的訪問。每個(gè)進(jìn)程中訪問臨界資源的代碼稱為臨界區(qū)。
這里為了介紹這種稍微復(fù)雜的概念。 再列出一個(gè)生產(chǎn)消費(fèi)者的例子 使用到了Queue隊(duì)列。
# coding:utf-8 import Queue import time import random import threading # write_lock = threading.Lock() # 創(chuàng)建primitive鎖對(duì)象用于控制輸出 class Producer(threading.Thread): # q傳遞一個(gè)隊(duì)列參數(shù), con傳遞了一個(gè)鏈接, name傳遞了一個(gè)名字 def __init__(self, q, name): super(Producer, self).__init__() self.q = q # self.con = con self.name = name print "Producer " + self.name + "Started" def run(self): while True: # 鎖對(duì)象常用的acquire獲得鎖方法和release釋放鎖方法 # 這里使用的是Thread的Condition對(duì)象 # self.con.acquire() if self.q.full(): print "Queue is full, producer wait!" # 手動(dòng)掛起,并且只能在獲得Lock的情況下才可以使用 否則會(huì)觸發(fā)RuntimeError # 調(diào)用wait()會(huì)釋放Lock 直到該線程被notify(),notifyall()或超時(shí)該線程又重新獲得Lock # self.con.wait() else: value = random.randint(0, 10) print self.name + " put " + str(value) + "into queue" self.q.put((self.name+":"+str(value))) # 放置到隊(duì)列中 # 通知消費(fèi)者,notify通知其他線程,被掛起的線程接到通知后會(huì)開始運(yùn)行 # 默認(rèn)通知一個(gè)正在等待該condition的線程,最多喚醒n個(gè)線程 必須在獲得Lock的情況下使用否則會(huì)報(bào)錯(cuò). # self.con.notify() # self.con.release() # 釋放鎖對(duì)象 class Consumer(threading.Thread): def __init__(self, q, name): super(Consumer, self).__init__() self.q = q # self.con = con self.name = name print "Consumer " + self.name + "started " def run(self): while True: # Condition常用的acquire獲得鎖方法和release釋放鎖方法 # self.con.acquire() if self.q.empty(): print "queue is empty, consumer wait!" # self.con.wait() else: value = self.q.get() # 從隊(duì)列中取消息 print self.name + " get " + value + "from queue" # 發(fā)送消息通知生產(chǎn)者 # self.con.notify() # self.con.release() # 釋放鎖對(duì)象 print "queue still have " + str(q.qsize()) + "task " if __name__ == "__main__": q = Queue.Queue(10) # 使用Condition對(duì)象可以在某些事件觸發(fā)或達(dá)到特定的條件后才處理數(shù)據(jù). # con = threading.Condition() # 兩個(gè)生產(chǎn)者 p1 = Producer(q, "P1") p2 = Producer(q, "P2") c1 = Consumer(q, "C1") p2.start() p1.start() c1.start()
若有問題歡迎指出
參考鏈接python threading模塊文檔翻譯: http://my.oschina.net/lionets/blog/194577?fromerr=pbWOeveo
多線程7經(jīng)典線程與互斥總結(jié):http://blog.csdn.net/dazhong159/article/details/7927034
《編寫高質(zhì)量代碼改善python程序的91個(gè)建議》第48和49建議。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37755.html
摘要:如果某線程并未使用很多操作,它會(huì)在自己的時(shí)間片內(nèi)一直占用處理器和。在中使用線程在和等大多數(shù)類系統(tǒng)上運(yùn)行時(shí),支持多線程編程。守護(hù)線程另一個(gè)避免使用模塊的原因是,它不支持守護(hù)線程。 這一篇是Python并發(fā)的第四篇,主要介紹進(jìn)程和線程的定義,Python線程和全局解釋器鎖以及Python如何使用thread模塊處理并發(fā) 引言&動(dòng)機(jī) 考慮一下這個(gè)場(chǎng)景,我們有10000條數(shù)據(jù)需要處理,處理每條...
摘要:默認(rèn)值為,指定為時(shí)代表可以阻塞,若同時(shí)指定,在超時(shí)時(shí)返回。當(dāng)消費(fèi)者線程調(diào)用意味著有消費(fèi)者取得任務(wù)并完成任務(wù),未完成的任務(wù)數(shù)就會(huì)減少。當(dāng)未完成的任務(wù)數(shù)降到,解除阻塞。 學(xué)習(xí)契機(jī) 最近的一個(gè)項(xiàng)目中在使用grpc時(shí)遇到一個(gè)問題,由于client端可多達(dá)200,每個(gè)端口每10s向grpc server發(fā)送一次請(qǐng)求,server端接受client的請(qǐng)求后根據(jù)request信息更新數(shù)據(jù)庫,再將數(shù)據(jù)...
摘要:批評(píng)的人通常都會(huì)說的多線程編程太困難了,眾所周知的全局解釋器鎖,或稱使得多個(gè)線程的代碼無法同時(shí)運(yùn)行。多線程起步首先讓我們來創(chuàng)建一個(gè)名為的模塊。多進(jìn)程可能比多線程更易使用,但需要消耗更大的內(nèi)存。 批評(píng) Python 的人通常都會(huì)說 Python 的多線程編程太困難了,眾所周知的全局解釋器鎖(Global Interpreter Lock,或稱 GIL)使得多個(gè)線程的 Python 代碼無...
摘要:因?yàn)樗蔷€程安全的,所以多個(gè)線程很輕松地使用同一個(gè)實(shí)例。后進(jìn)先出隊(duì)列使用后進(jìn)先出順序,與棧結(jié)構(gòu)相似這就是全部代碼了,這正是設(shè)計(jì)很棒的一個(gè)原因,它將底層的數(shù)據(jù)操作抽象成四個(gè)操作函數(shù),本身來處理線程安全的問題,使得其子類只需關(guān)注底層的操作。 起步 queue 模塊提供適用于多線程編程的先進(jìn)先出(FIFO)數(shù)據(jù)結(jié)構(gòu)。因?yàn)樗蔷€程安全的,所以多個(gè)線程很輕松地使用同一個(gè)實(shí)例。 源碼分析 先從初始...
摘要:擴(kuò)展支持多用戶并發(fā)訪問與線程池。項(xiàng)目請(qǐng)見初學(xué)網(wǎng)絡(luò)編程之服務(wù)器。不允許超過磁盤配額。該文件是一個(gè)使用模塊編寫的線程池類。這一步就做到了線程池的作用。 對(duì)MYFTP項(xiàng)目進(jìn)行升級(jí)。擴(kuò)展支持多用戶并發(fā)訪問與線程池。MYFTP項(xiàng)目請(qǐng)見python初學(xué)——網(wǎng)絡(luò)編程之FTP服務(wù)器。 擴(kuò)展需求 1.在之前開發(fā)的FTP基礎(chǔ)上,開發(fā)支持多并發(fā)的功能2.不能使用SocketServer模塊,必須自己實(shí)現(xiàn)多線...
閱讀 2624·2021-11-22 12:01
閱讀 1141·2021-11-15 11:37
閱讀 3735·2021-09-22 14:59
閱讀 1786·2021-09-04 16:45
閱讀 1419·2021-09-03 10:30
閱讀 1063·2021-08-11 11:18
閱讀 2497·2019-08-30 10:53
閱讀 2047·2019-08-29 15:13