python獲取線程
『壹』 怎樣在python中捕獲線程拋出的異常
python的線程中的異常,通常不會給你顯示出錯的語句。你可以將thread函數,或者是Thread的run里的內容用整個兒的try catch包裹起來。搜索
然後這樣
import traceback,sys
try:
threadfun1()
except:
traceback.print_exc(file=sys.stdout)
這樣出錯的時候就能定位到是哪一行代碼了。
因為線程經常出現這種無顯示錯誤位置的情形。後來都形成了習慣,要不把線程放在try catch里。要不就仔細檢查線程函數,確保它沒有錯誤,再放出去運行。
另外你還可以將線程函數的功能放在主進程里,單線程運行。這樣錯誤位置就曝露出來了。
僅僅從你這個提示來看是無法定位錯誤的位置與類型的。
『貳』 python - 日誌記錄模塊(logging)的二次封裝
上篇文章 對logging做了基本介紹,我們可以使用logging來做日誌的簡單記錄。但實際項目應用時,我們一般會根據自身需要對其做二次封裝(loggingV2),然後在其他python文件中, 先import申明後直接調用。
廢話不多說,下面給幾個二次封裝的簡單示例:
示例一:
loggingV2.py - 封裝
logMain.py - 應用
示例二:
對上述示例進行 模塊化封局凳裝 ,如下log.py
則任何聲明了log模塊的python文件都可以調用logging日誌系統,如下logMain.py
示例三:
對上述示例進行 定製化封裝 ,如下myLog.py
需求:
1)同時實現終端顯示與日誌文件保存
2)日誌文件名除日期外,增加顯示時間,精確到秒
3)日誌輸出級別可配置
4)日誌保存路徑與文件名可配置
5)日誌跨天(或者小時/分鍾),另生成新文件保存
改寫logMain.py,如下:
示例四:
對上述示例進行 非同步線程封裝 ,如下myThreadLog.py
需求:
1)獨立線程處理日誌,不影響主程序性能
2)使用隊列緩嫌非同步處理日誌記錄
繼續改寫logMain.py,如下:
注意 - 線程相關操作函數(如下):
1.threading.Thread() — 創建線程並初始化線程,可以為線程傳遞參數
2.threading.enumerate() — 返回一個包含正在運行的線程的list
3.threading.activeCount(): 返回正桐哪旅在運行的線程數量,與len(threading.enumerate())有相同的結果
4.Thread.start() — 啟動線程
5.Thread.join() — 阻塞函數,一直等到線程結束
6.Thread.isAlive() — 返回線程活動狀態
7.Thread.setName() — 設置線程名
8.Thread.getName() — 獲取線程名
9.Thread.setDaemon() — 設置為後台線程,這里默認是False,設置為True之後則主線程不會再等待子線程結束才結束,而是主線程結束意味程序退出,子線程也立即結束,注意調用時必須設置在start()之前;
10.除了以上常用函數,線程還經常與互斥鎖Lock/事件Event/信號量Condition/隊列Queue等函數配合使用
『叄』 python如何獲取進程和線程狀態
threading.active_count()
Return the number of Thread objects currently alive. The returned count is equal to the length of the list returned by enumerate().
active_count可以返回當前活動的線程枚舉
我一般是這么用的
def getHeatsParallel(self): threads = [] for i in range(0, self.threadCount): t = threading.Thread(target=self.SomeFunction, name=str(i)) threads.append(t) t.start() for t in threads: t.join()
『肆』 python之多線程
進程的概念:以一個整體的形式暴露給操作系統管理,裡麵包含各種資源的調用。 對各種資源管理的集合就可以稱為進程。
線程的概念:是操作系統能夠進行運算調度的最小單位。本質上就是一串指令的集合。
進程和線程的區別:
1、線程共享內存空間,進程有獨立的內存空間。
2、線程啟動速度快,進程啟動速度慢。注意:二者的運行速度是無法比較的。
3、線程是執行的指令集,進程是資源的集合
4、兩個子進程之間數據不共享,完全獨立。同一個進程下的線程共享同一份數據。
5、創建新的線程很簡單,創建新的進程需要對他的父進程進行一次克隆。
6、一個線程可以操作(控制)同一進程里的其他線程,但是進程只能操作子進程
7、同一個進程的線程可以直接交流,兩個進程想要通信,必須通過一個中間代理來實現。
8、對於線程的修改,可能會影響到其他線程的行為。但是對於父進程的修改不會影響到子進程。
第一個程序,使用循環來創建線程,但是這個程序中一共有51個線程,我們創建了50個線程,但是還有一個程序本身的線程,是主線程。這51個線程是並行的。注意:這個程序中是主線程啟動了子線程。
相比上個程序,這個程序多了一步計算時間,但是我們觀察結果會發現,程序顯示的執行時間只有0.007秒,這是因為最後一個print函數它存在於主線程,而整個程序主線程和所有子線程是並行的,那麼可想而知,在子線程還沒有執行完畢的時候print函數就已經執行了,總的來說,這個時間只是執行了一個線程也就是主線程所用的時間。
接下來這個程序,吸取了上面這個程序的缺點,創建了一個列表,把所有的線程實例都存進去,然後使用一個for循環依次對線程實例調用join方法,這樣就可以使得主線程等待所創建的所有子線程執行完畢才能往下走。 注意實驗結果:和兩個線程的結果都是兩秒多一點
注意觀察實驗結果,並沒有執行列印task has done,並且程序執行時間極其短。
這是因為在主線程啟動子線程前把子線程設置為守護線程。
只要主線程執行完畢,不管子線程是否執行完畢,就結束。但是會等待非守護線程執行完畢
主線程退出,守護線程全部強制退出。皇帝死了,僕人也跟著殉葬
應用的場景 : socket-server
注意:gil只是為了減低程序開發復雜度。但是在2.幾的版本上,需要加用戶態的鎖(gil的缺陷)而在3點幾的版本上,加鎖不加鎖都一樣。
下面這個程序是一個典型的生產者消費者模型。
生產者消費者模型是經典的在開發架構中使用的模型
運維中的集群就是生產者消費者模型,生活中很多都是
那麼,多線程的使用場景是什麼?
python中的多線程實質上是對上下文的不斷切換,可以說是假的多線程。而我們知道,io操作不佔用cpu,計算佔用cpu,那麼python的多線程適合io操作密集的任務,比如socket-server,那麼cpu密集型的任務,python怎麼處理?python可以折中的利用計算機的多核:啟動八個進程,每個進程有一個線程。這樣就可以利用多進程解決多核問題。
『伍』 python之多線程原理
並發:邏輯上具備同時處理多個任務的能力。
並行:物理上在同一時刻執行多個並發任務。
舉例:開個QQ,開了一個進程,開了微信,開了一個進程。在QQ這個進程裡面,傳輸文字開一個線程、傳輸語音開了一個線程、彈出對話框又開了一個線程。
總結:開一個軟體,相當於開了一個進程。在這個軟體運行的過程里,多個工作同時運轉,完成了QQ的運行,那麼這個多個工作分別有多個線程。
線程和進程之間的區別:
進程在python中的使用,對模塊threading進行操作,調用的這個三方庫。可以通過 help(threading) 了解其中的方法、變數使用情況。也可以使用 dir(threading) 查看目錄結構。
current_thread_num = threading.active_count() # 返回正在運行的線程數量
run_thread_len = len(threading.enumerate()) # 返回正在運行的線程數量
run_thread_list = threading.enumerate() # 返回當前運行線程的列表
t1=threading.Thread(target=dance) #創建兩個子線程,參數傳遞為函數名
t1.setDaemon(True) # 設置守護進程,守護進程:主線程結束時自動退出子線程。
t1.start() # 啟動子線程
t1.join() # 等待進程結束 exit()`# 主線程退出,t1子線程設置了守護進程,會自動退出。其他子線程會繼續執行。
『陸』 Python:進程(threading)
這里是自己寫下關於 Python 跟進程相關的 threading 模塊的一點筆記,跟有些跟 Linux 調用挺像的,有共通之處。
https://docs.python.org/3/library/threading.html?highlight=threading#thread-objects
直接傳入
繼承 Thread 重寫 run 方法
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
group 線程組,未實現
start() 線程就緒
join([timeout]) 阻塞其他線程,直到調用這方法的進程結束或時間到達
RuntimeError: cannot join thread before it is started
get/setName(name) 獲取/設置線程名。
isAlive() 返回線程是否在運行。
is/setDaemon(bool): 獲取/設置是後台線程(默認前台線程(False))。(在start之前設置)
The entire Python program exits when no alive non-daemon threads are left.
沒有非後台進程運行,Python 就退出。
主線程執行完畢後,後台線程不管是成功與否,主線程均停止
t.start()
t.join()
start() 後 join() 會順序執行,失去線程意義
https://docs.python.org/3/library/threading.html?#lock-objects
Lock屬於全局,Rlock屬於線程(R的意思是可重入,線程用Lock的話會死鎖,來看例子)
acquire(blocking=True, timeout=-1) 申請鎖,返回申請的結果
release() 釋放鎖,沒返回結果
https://docs.python.org/3/library/threading.html#condition-objects
可以在構造時傳入rlock lock實例,不然自己生成一個。
acquire([timeout])/release(): 與lock rlock 相同
wait([timeout]): 調用這個方法將使線程進入等待池,並釋放鎖。調用方法前線程必須已獲得鎖定,否則將拋出異常。
notify(): 調用這個方法將從等待池挑選一個線程並通知,收到通知的線程將自動調用acquire()嘗試獲得鎖定(進入鎖定池);其他線程仍然在等待池中。調用這個方法不會釋放鎖定。調用方法前線程必須已獲得鎖定,否則將拋出異常。
notifyAll(): 調用這個方法將通知等待池中所有的線程,這些線程都將進入鎖定池嘗試獲得鎖定。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
threading.Semaphore(value=1)
https://docs.python.org/3/library/threading.html#semaphore-objects
acquire(blocking=True, timeout=None)
資源數大於0,減一並返回,等於0時等待,blocking為False不阻塞進程
返回值是申請結果
release()
資源數加1
https://docs.python.org/3/library/threading.html#event-objects
事件內置了一個初始為False的標志
is_set() 返回內置標志的狀態
set() 設為True
clear() 設為False
wait(timeout=None) 阻塞線程並等待,為真時返回。返回值只會在等待超時時為False,其他情況為True
https://docs.python.org/3/library/threading.html#timer-objects
threading.Timer(interval, function, args=None, kwargs=None)
第一個參數是時間間隔,單位是秒,整數或者浮點數,負數不會報錯直接執行不等待
可以用cancel() 取消
https://docs.python.org/3/library/threading.html#barrier-objects
threading.Barrier(parties, action=None, timeout=None)
調用的進程數目達到第一個設置的參數就喚醒全部進程
wait(timeout=None)
reset() 重置,等待中的進程收到 BrokenBarrierError 錯誤
『柒』 一篇文章帶你深度解析Python線程和進程
使用Python中的線程模塊,能夠同時運行程序的不同部分,並簡化設計。如果你已經入門Python,並且想用線程來提升程序運行速度的話,希望這篇教程會對你有所幫助。
線程與進程
什麼是進程
進程是系統進行資源分配和調度的一個獨立單位 進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,不同進程通過進程間通信來通信。由於進程比較重量,占據獨立的內存,所以上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。
什麼是線程
CPU調度和分派的基本單位 線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。
進程與線程的關系圖
線程與進程的區別:
進程
現實生活中,有很多的場景中的事情是同時進行的,比如開車的時候 手和腳共同來駕駛 汽車 ,比如唱歌跳舞也是同時進行的,再比如邊吃飯邊打電話;試想如果我們吃飯的時候有一個領導來電,我們肯定是立刻就接聽了。但是如果你吃完飯再接聽或者回電話,很可能會被開除。
注意:
多任務的概念
什麼叫 多任務 呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽MP3,一邊在用Word趕作業,這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在後台同時運行著,只是桌面上沒有顯示而已。
現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多任務。由於CPU執行代碼都是順序執行的,那麼,單核CPU是怎麼執行多任務的呢?
答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒,這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的並行執行多任務只能在多核CPU上實現,但是,由於任務數量遠遠多於CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。 其實就是CPU執行速度太快啦!以至於我們感受不到在輪流調度。
並行與並發
並行(Parallelism)
並行:指兩個或兩個以上事件(或線程)在同一時刻發生,是真正意義上的不同事件或線程在同一時刻,在不同CPU資源呢上(多核),同時執行。
特點
並發(Concurrency)
指一個物理CPU(也可以多個物理CPU) 在若幹道程序(或線程)之間多路復用,並發性是對有限物理資源強制行使多用戶共享以提高效率。
特點
multiprocess.Process模塊
process模塊是一個創建進程的模塊,藉助這個模塊,就可以完成進程的創建。
語法:Process([group [, target [, name [, args [, kwargs]]]]])
由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)。
注意:1. 必須使用關鍵字方式來指定參數;2. args指定的為傳給target函數的位置參數,是一個元祖形式,必須有逗號。
參數介紹:
group:參數未使用,默認值為None。
target:表示調用對象,即子進程要執行的任務。
args:表示調用的位置參數元祖。
kwargs:表示調用對象的字典。如kwargs = {'name':Jack, 'age':18}。
name:子進程名稱。
代碼:
除了上面這些開啟進程的方法之外,還有一種以繼承Process的方式開啟進程的方式:
通過上面的研究,我們千方百計實現了程序的非同步,讓多個任務可以同時在幾個進程中並發處理,他們之間的運行沒有順序,一旦開啟也不受我們控制。盡管並發編程讓我們能更加充分的利用IO資源,但是也給我們帶來了新的問題。
當多個進程使用同一份數據資源的時候,就會引發數據安全或順序混亂問題,我們可以考慮加鎖,我們以模擬搶票為例,來看看數據安全的重要性。
加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串列的修改。加鎖犧牲了速度,但是卻保證了數據的安全。
因此我們最好找尋一種解決方案能夠兼顧:1、效率高(多個進程共享一塊內存的數據)2、幫我們處理好鎖問題。
mutiprocessing模塊為我們提供的基於消息的IPC通信機制:隊列和管道。隊列和管道都是將數據存放於內存中 隊列又是基於(管道+鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來, 我們應該盡量避免使用共享數據,盡可能使用消息傳遞和隊列,避免處理復雜的同步和鎖問題,而且在進程數目增多時,往往可以獲得更好的可獲展性( 後續擴展該內容 )。
線程
Python的threading模塊
Python 供了幾個用於多線程編程的模塊,包括 thread, threading 和 Queue 等。thread 和 threading 模塊允許程序員創建和管理線程。thread 模塊 供了基本的線程和鎖的支持,而 threading 供了更高級別,功能更強的線程管理的功能。Queue 模塊允許用戶創建一個可以用於多個線程之間 共享數據的隊列數據結構。
python創建和執行線程
創建線程代碼
1. 創建方法一:
2. 創建方法二:
進程和線程都是實現多任務的一種方式,例如:在同一台計算機上能同時運行多個QQ(進程),一個QQ可以打開多個聊天窗口(線程)。資源共享:進程不能共享資源,而線程共享所在進程的地址空間和其他資源,同時,線程有自己的棧和棧指針。所以在一個進程內的所有線程共享全局變數,但多線程對全局變數的更改會導致變數值得混亂。
代碼演示:
得到的結果是:
首先需要明確的一點是GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就好比C++是一套語言(語法)標准,但是可以用不同的編譯器來編譯成可執行代碼。同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行(其中的JPython就沒有GIL)。
那麼CPython實現中的GIL又是什麼呢?GIL全稱Global Interpreter Lock為了避免誤導,我們還是來看一下官方給出的解釋:
主要意思為:
因此,解釋器實際上被一個全局解釋器鎖保護著,它確保任何時候都只有一個Python線程執行。在多線程環境中,Python 虛擬機按以下方式執行:
由於GIL的存在,Python的多線程不能稱之為嚴格的多線程。因為 多線程下每個線程在執行的過程中都需要先獲取GIL,保證同一時刻只有一個線程在運行。
由於GIL的存在,即使是多線程,事實上同一時刻只能保證一個線程在運行, 既然這樣多線程的運行效率不就和單線程一樣了嗎,那為什麼還要使用多線程呢?
由於以前的電腦基本都是單核CPU,多線程和單線程幾乎看不出差別,可是由於計算機的迅速發展,現在的電腦幾乎都是多核CPU了,最少也是兩個核心數的,這時差別就出來了:通過之前的案例我們已經知道,即使在多核CPU中,多線程同一時刻也只有一個線程在運行,這樣不僅不能利用多核CPU的優勢,反而由於每個線程在多個CPU上是交替執行的,導致在不同CPU上切換時造成資源的浪費,反而會更慢。即原因是一個進程只存在一把gil鎖,當在執行多個線程時,內部會爭搶gil鎖,這會造成當某一個線程沒有搶到鎖的時候會讓cpu等待,進而不能合理利用多核cpu資源。
但是在使用多線程抓取網頁內容時,遇到IO阻塞時,正在執行的線程會暫時釋放GIL鎖,這時其它線程會利用這個空隙時間,執行自己的代碼,因此多線程抓取比單線程抓取性能要好,所以我們還是要使用多線程的。
GIL對多線程Python程序的影響
程序的性能受到計算密集型(CPU)的程序限制和I/O密集型的程序限制影響,那什麼是計算密集型和I/O密集型程序呢?
計算密集型:要進行大量的數值計算,例如進行上億的數字計算、計算圓周率、對視頻進行高清解碼等等。這種計算密集型任務雖然也可以用多任務完成,但是花費的主要時間在任務切換的時間,此時CPU執行任務的效率比較低。
IO密集型:涉及到網路請求(time.sleep())、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和內存的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。
當然為了避免GIL對我們程序產生影響,我們也可以使用,線程鎖。
Lock&RLock
常用的資源共享鎖機制:有Lock、RLock、Semphore、Condition等,簡單給大家分享下Lock和RLock。
Lock
特點就是執行速度慢,但是保證了數據的安全性
RLock
使用鎖代碼操作不當就會產生死鎖的情況。
什麼是死鎖
死鎖:當線程A持有獨占鎖a,並嘗試去獲取獨占鎖b的同時,線程B持有獨占鎖b,並嘗試獲取獨占鎖a的情況下,就會發生AB兩個線程由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱為死鎖。即死鎖是指多個進程因競爭資源而造成的一種僵局,若無外力作用,這些進程都將無法向前推進。
所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確定資源的合理分配演算法,避免進程永久占據系統資源。
死鎖代碼
python線程間通信
如果各個線程之間各干各的,確實不需要通信,這樣的代碼也十分的簡單。但這一般是不可能的,至少線程要和主線程進行通信,不然計算結果等內容無法取回。而實際情況中要復雜的多,多個線程間需要交換數據,才能得到正確的執行結果。
python中Queue是消息隊列,提供線程間通信機制,python3中重名為為queue,queue模塊塊下提供了幾個阻塞隊列,這些隊列主要用於實現線程通信。
在 queue 模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在於進隊列、出隊列的不同。
簡單代碼演示
此時代碼會阻塞,因為queue中內容已滿,此時可以在第四個queue.put('蘋果')後面添加timeout,則成為 queue.put('蘋果',timeout=1)如果等待1秒鍾仍然是滿的就會拋出異常,可以捕獲異常。
同理如果隊列是空的,無法獲取到內容默認也會阻塞,如果不阻塞可以使用queue.get_nowait()。
在掌握了 Queue 阻塞隊列的特性之後,在下面程序中就可以利用 Queue 來實現線程通信了。
下面演示一個生產者和一個消費者,當然都可以多個
使用queue模塊,可在線程間進行通信,並保證了線程安全。
協程
協程,又稱微線程,纖程。英文名Coroutine。
協程是python個中另外一種實現多任務的方式,只不過比線程更小佔用更小執行單元(理解為需要的資源)。為啥說它是一個執行單元,因為它自帶CPU上下文。這樣只要在合適的時機, 我們可以把一個協程 切換到另一個協程。只要這個過程中保存或恢復 CPU上下文那麼程序還是可以運行的。
通俗的理解:在一個線程中的某個函數,可以在任何地方保存當前函數的一些臨時變數等信息,然後切換到另外一個函數中執行,注意不是通過調用函數的方式做到的,並且切換的次數以及什麼時候再切換到原來的函數都由開發者自己確定。
在實現多任務時,線程切換從系統層面遠不止保存和恢復 CPU上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存Cache等等數據,操作系統還會幫你做這些數據的恢復操作。所以線程的切換非常耗性能。但是協程的切換只是單純的操作CPU的上下文,所以一秒鍾切換個上百萬次系統都抗的住。
greenlet與gevent
為了更好使用協程來完成多任務,除了使用原生的yield完成模擬協程的工作,其實python還有的greenlet模塊和gevent模塊,使實現協程變的更加簡單高效。
greenlet雖說實現了協程,但需要我們手工切換,太麻煩了,gevent是比greenlet更強大的並且能夠自動切換任務的模塊。
其原理是當一個greenlet遇到IO(指的是input output 輸入輸出,比如網路、文件操作等)操作時,比如訪問網路,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。
模擬耗時操作:
如果有耗時操作也可以換成,gevent中自己實現的模塊,這時候就需要打補丁了。
使用協程完成一個簡單的二手房信息的爬蟲代碼吧!
以下文章來源於Python專欄 ,作者宋宋
文章鏈接:https://mp.weixin.qq.com/s/2r3_ipU3HjdA5VnqSHjUnQ
『捌』 Python實現簡單多線程任務隊列
Python實現簡單多線程任務隊列
最近我在用梯度下降演算法繪制神經網路的數據時,遇到了一些演算法性能的問題。梯度下降演算法的代碼如下(偽代碼):
defgradient_descent(): # the gradient descent code plotly.write(X, Y)
一般來說,當網路請求 plot.ly 繪圖時會阻塞等待返回,於是也會影響到其他的梯度下降函數的執行速度。
一種解決辦法是每調用一次 plotly.write 函數就開啟一個新的線程,但是這種方法感覺不是很好。 我不想用一個像 cerely(一種分布式任務隊列)一樣大而全的任務隊列框架,因為框架對於我的這點需求來說太重了,並且我的繪圖也並不需要 redis 來持久化數據。
那用什麼辦法解決呢?我在 python 中寫了一個很小的任務隊列,它可以在一個單獨的線程中調用 plotly.write函數。下面是程序代碼。
classTaskQueue(Queue.Queue):
首先我們繼承 Queue.Queue 類。從 Queue.Queue 類可以繼承 get 和 put 方法,以及隊列的行為。
def__init__(self, num_workers=1): Queue.Queue.__init__(self) self.num_workers=num_workers self.start_workers()
初始化的時候,我們可以不用考慮工作線程的數量。
defadd_task(self, task,*args,**kwargs): args=argsor() kwargs=kwargsor{} self.put((task, args, kwargs))
我們把 task, args, kwargs 以元組的形式存儲在隊列中。*args 可以傳遞數量不等的參數,**kwargs 可以傳遞命名參數。
defstart_workers(self): foriinrange(self.num_workers): t=Thread(target=self.worker) t.daemon=True t.start()
我們為每個 worker 創建一個線程,然後在後台刪除。
下面是 worker 函數的代碼:
defworker(self): whileTrue: tupl=self.get() item, args, kwargs=self.get() item(*args,**kwargs) self.task_done()
worker 函數獲取隊列頂端的任務,並根據輸入參數運行,除此之外,沒有其他的功能。下面是隊列的代碼:
我們可以通過下面的代碼測試:
defblokkah(*args,**kwargs): time.sleep(5) print「Blokkah mofo!」 q=TaskQueue(num_workers=5) foriteminrange(1): q.add_task(blokkah) q.join()# wait for all the tasks to finish. print「Alldone!」
Blokkah 是我們要做的任務名稱。隊列已經緩存在內存中,並且沒有執行很多任務。下面的步驟是把主隊列當做單獨的進程來運行,這樣主程序退出以及執行資料庫持久化時,隊列任務不會停止運行。但是這個例子很好地展示了如何從一個很簡單的小任務寫成像工作隊列這樣復雜的程序。
defgradient_descent(): # the gradient descent code queue.add_task(plotly.write, x=X, y=Y)
修改之後,我的梯度下降演算法工作效率似乎更高了。如果你很感興趣的話,可以參考下面的代碼。 classTaskQueue(Queue.Queue): def__init__(self, num_workers=1):Queue.Queue.__init__(self)self.num_workers=num_workersself.start_workers() defadd_task(self, task,*args,**kwargs):args=argsor()kwargs=kwargsor{}self.put((task, args, kwargs)) defstart_workers(self):foriinrange(self.num_workers):t=Thread(target=self.worker)t.daemon=Truet.start() defworker(self):whileTrue:tupl=self.get()item, args, kwargs=self.get()item(*args,**kwargs)self.task_done() deftests():defblokkah(*args,**kwargs):time.sleep(5)print"Blokkah mofo!" q=TaskQueue(num_workers=5) foriteminrange(10):q.add_task(blokkah) q.join()# block until all tasks are doneprint"All done!" if__name__=="__main__":tests()
『玖』 python 線程池的使用
最近在做一個爬蟲相關的項目,單線程的整站爬蟲,耗時真的不是一般的巨大,運行一次也是心累,,,所以,要想實現整站爬蟲,多線程是不可避免的,那麼python多線程又應該怎樣實現呢?這里主要要幾個問題(關於python多線程的GIL問題就不再說了,網上太多了)。
一、 既然多線程可以縮短程序運行時間,那麼,是不是線程數量越多越好呢?
顯然,並不是,每一個線程的從生成到消亡也是需要時間和資源的,太多的線程會佔用過多的系統資源(內存開銷,cpu開銷),而且生成太多的線程時間也是可觀的,很可能會得不償失,這里給出一個最佳線程數量的計算方式:
最佳線程數的獲取:
1、通過用戶慢慢遞增來進行性能壓測,觀察QPS(即每秒的響應請求數,也即是最大吞吐能力。),響應時間
2、根據公式計算:伺服器端最佳線程數量=((線程等待時間+線程cpu時間)/線程cpu時間) * cpu數量
3、單用戶壓測,查看CPU的消耗,然後直接乘以百分比,再進行壓測,一般這個值的附近應該就是最佳線程數量。
二、為什麼要使用線程池?
對於任務數量不斷增加的程序,每有一個任務就生成一個線程,最終會導致線程數量的失控,例如,整站爬蟲,假設初始只有一個鏈接a,那麼,這個時候只啟動一個線程,運行之後,得到這個鏈接對應頁面上的b,c,d,,,等等新的鏈接,作為新任務,這個時候,就要為這些新的鏈接生成新的線程,線程數量暴漲。在之後的運行中,線程數量還會不停的增加,完全無法控制。所以,對於任務數量不端增加的程序,固定線程數量的線程池是必要的。
三、如何使用線程池
過去使用threadpool模塊,現在一般使用concurrent.futures模塊,這個模塊是python3中自帶的模塊,但是,python2.7以上版本也可以安裝使用,具體使用方式如下:
注意到:
concurrent.futures.ThreadPoolExecutor,在提交任務的時候,有兩種方式,一種是submit()函數,另一種是map()函數,兩者的主要區別在於:
『拾』 python 多線程
python支持多線程效果還不錯,很多方面都用到了python 多線程的知識,我前段時間用python 多線程寫了個處理生產者和消費者的問題,把代碼貼出來給你看下:
#encoding=utf-8
import threading
import random
import time
from Queue import Queue
class Procer(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()
procer = Procer('Procer', queue)
consumer = Consumer('Consumer', queue)
print 'Starting threads ...'
procer.start()
consumer.start()
procer.join()
consumer.join()
print 'All threads have terminated.'
if __name__ == '__main__':
main()
如果你想要了解更多的python 多線程知識可以點下面的參考資料的地址,希望對有幫助!