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