① python如何實現線程池
#這個類是線程類,用來在主程序中調用生成一個線程。其實線程池就是線程的集合地,
#能夠解決有效統一的管理線程,基本就達到了線程池的目的;
#這一段代碼是我的爬蟲程序中的一部分,希望對你有用。
classSpider(Thread):
def__init__(self,todo_list):
super().__init__()
self.setDaemon(True)
self.todo_list=todo_list
self.stat=IDLE
defis_idle(self):
returnself.stat==IDLE
defrun(self):
whileTrue:
url=self.todo_list.get()
#開始線程工作
#這個函數就是主函數了,
defmain(max_threads):
########這里和上一個函數就是核心代碼了。
#創建N個線程,並啟動
print('Spawnspiders')
spiders=[Spider(todo_list)foriinrange(max_threads)]
forspdinspiders:
spd.start()
#python主運行代碼:
if__name__=='__main__':
main(max_threads)
只能給你這么多解釋了,如果想弄懂,還是要去看看基礎知識的。
另外可以查一下有沒有封裝好的三方庫。
② Python中的線程池是什麼
多線程的做法是,可以同時創建多個線程放入等待執行的序列中。某個線程執行完畢就將它從序列中移除並銷毀。不然的話,即時創建,然後就一定要等到它銷毀,那這不是多線程,這是單線程.
進程中根據需要,為一些需要慢資源、竟爭性資源的任務創建線程,排除等候執行。
線程需要等待分配,如果短時間建立了多個線程,哪個線程先開始執行,由調度程序決定;
...調度>>>執行>>>循環
當線程執行完畢,銷毀線程。
比如說,下載圖片:我有一個列表,記錄了要下載的300張圖片的URL。每個圖片的來源可能是不同網站(伺服器)。那麼,主循環里只需要創建300個【下載】線程。每個線程負責一個URL的下載任務。
然後,調序程序開始調度:線程1有數據過來了,分配時間片給線程1處理這段數據...線程n執行完畢,銷毀線程n...線程1又有數據過來了,分配時間片給線程1處理這段數據......銷毀線程n,沒有等待中的線程,調度暫停。
於是,所有的圖片下載完了。
這個過程與單線程的不同是,在多線程中,最開始同一時間有300個請求在等待若干個伺服器返回數據,而單線程則總是只有一個請求在等待伺服器返回數據或者正在處理數據,另外299個請求根本不存在。
這才是多線程與單線程最主要的差別:在等待某個資源的時候,把其它資源給別的線程去使用。
③ 小白都看懂了,Python 中的線程和進程精講,建議收藏
目錄
眾所周知,CPU是計算機的核心,它承擔了所有的計算任務。而操作系統是計算機的管理者,是一個大管家,它負責任務的調度,資源的分配和管理,統領整個計算機硬體。應用程序是具有某種功能的程序,程序運行與操作系統之上
在很早的時候計算機並沒有線程這個概念,但是隨著時代的發展,只用進程來處理程序出現很多的不足。如當一個進程堵塞時,整個程序會停止在堵塞處,並且如果頻繁的切換進程,會浪費系統資源。所以線程出現了
線程是能擁有資源和獨立運行的最小單位,也是程序執行的最小單位。一個進程可以擁有多個線程,而且屬於同一個進程的多個線程間會共享該進行的資源
① 200 多本 Python 電子書(和經典的書籍)應該有
② Python標准庫資料(最全中文版)
③ 項目源碼(四五十個有趣且可靠的練手項目及源碼)
④ Python基礎入門、爬蟲、網路開發、大數據分析方面的視頻(適合小白學習)
⑤ Python學習路線圖(告別不入流的學習)
私信我01即可獲取大量Python學習資源
進程時一個具有一定功能的程序在一個數據集上的一次動態執行過程。進程由程序,數據集合和進程式控制制塊三部分組成。程序用於描述進程要完成的功能,是控制進程執行的指令集;數據集合是程序在執行時需要的數據和工作區;程序控制塊(PCB)包含程序的描述信息和控制信息,是進程存在的唯一標志
在Python中,通過兩個標准庫 thread 和 Threading 提供對線程的支持, threading 對 thread 進行了封裝。 threading 模塊中提供了 Thread , Lock , RLOCK , Condition 等組件
在Python中線程和進程的使用就是通過 Thread 這個類。這個類在我們的 thread 和 threading 模塊中。我們一般通過 threading 導入
默認情況下,只要在解釋器中,如果沒有報錯,則說明線程可用
守護模式:
現在我們程序代碼中,有多個線程, 並且在這個幾個線程中都會去 操作同一部分內容,那麼如何實現這些數據的共享呢?
這時,可以使用 threading庫裡面的鎖對象 Lock 去保護
Lock 對象的acquire方法 是申請鎖
每個線程在操作共享數據對象之前,都應該申請獲取操作權,也就是調用該共享數據對象對應的鎖對象的acquire方法,如果線程A 執行了 acquire() 方法,別的線程B 已經申請到了這個鎖, 並且還沒有釋放,那麼 線程A的代碼就在此處 等待 線程B 釋放鎖,不去執行後面的代碼。
直到線程B 執行了鎖的 release 方法釋放了這個鎖, 線程A 才可以獲取這個鎖,就可以執行下面的代碼了
如:
到在使用多線程時,如果數據出現和自己預期不符的問題,就可以考慮是否是共享的數據被調用覆蓋的問題
使用 threading 庫裡面的鎖對象 Lock 去保護
Python中的多進程是通過multiprocessing包來實現的,和多線程的threading.Thread差不多,它可以利用multiprocessing.Process對象來創建一個進程對象。這個進程對象的方法和線程對象的方法差不多也有start(), run(), join()等方法,其中有一個方法不同Thread線程對象中的守護線程方法是setDeamon,而Process進程對象的守護進程是通過設置daemon屬性來完成的
守護模式:
其使用方法和線程的那個 Lock 使用方法類似
Manager的作用是提供多進程共享的全局變數,Manager()方法會返回一個對象,該對象控制著一個服務進程,該進程中保存的對象運行其他進程使用代理進行操作
語法:
線程池的基類是 concurrent.futures 模塊中的 Executor , Executor 提供了兩個子類,即 ThreadPoolExecutor 和 ProcessPoolExecutor ,其中 ThreadPoolExecutor 用於創建線程池,而 ProcessPoolExecutor 用於創建進程池
如果使用線程池/進程池來管理並發編程,那麼只要將相應的 task 函數提交給線程池/進程池,剩下的事情就由線程池/進程池來搞定
Exectuor 提供了如下常用方法:
程序將 task 函數提交(submit)給線程池後,submit 方法會返回一個 Future 對象,Future 類主要用於獲取線程任務函數的返回值。由於線程任務會在新線程中以非同步方式執行,因此,線程執行的函數相當於一個「將來完成」的任務,所以 Python 使用 Future 來代表
Future 提供了如下方法:
使用線程池來執行線程任務的步驟如下:
最佳線程數目 = ((線程等待時間+線程CPU時間)/線程CPU時間 )* CPU數目
也可以低於 CPU 核心數
使用線程池來執行線程任務的步驟如下:
關於進程的開啟代碼一定要放在 if __name__ == '__main__': 代碼之下,不能放到函數中或其他地方
開啟進程的技巧
開啟進程的數量最好低於最大 CPU 核心數
④ python線程池ThreadPoolExecutor.submit的數據丟失問題
ThreadPoolExecutor 是 Executor 的子類,它使用線程池來非同步執行調用。
關於concurrent.futures模塊下的ThreadPoolExecutor類
在使用submit的時候,如果參數傳進去的是生成器對象,在某些情況下,生成器對象會被消耗掉一部分或者是全部的數據
以上示例中,嘗試二部分是正常且保證是沒有問題
而嘗試一則會在submit的時候被消耗掉一部分的數據
嘗試三這里先利用緩弊汪tee,復制出兩個副本,並且調用了其中一個轉list,另一個丟給submit方法,卜辯這種情況下,數據不會產生丟失
問擾仔題一 :生成器對象為什麼會在submit的時候,丟失了部分數據?
問題二 :嘗試三這里復制了副本,對其中一個轉list,就不會丟失數據,不轉list還是會丟失數據,又是什麼原理?
不知道有沒知情人士可以幫忙解答下,不勝感激、
⑤ Python多線程總結
在實際處理數據時,因系統內存有限,我們不可能一次把所有數據都導出進行操作,所以需要批量導出依次操作。為了加快運行,我們會採用多線程的方法進行數據處理, 以下為我總結的多線程批量處理數據的模板:
主要分為三大部分:
共分4部分對多線程的內容進行總結。
先為大家介紹線程的相關概念:
在飛車程序中,如果沒有多線程,我們就不能一邊聽歌一邊玩飛車,聽歌與玩 游戲 不能並行;在使用多線程後,我們就可以在玩 游戲 的同時聽背景音樂。在這個例子中啟動飛車程序就是一個進程,玩 游戲 和聽音樂是兩個線程。
Python 提供了 threading 模塊來實現多線程:
因為新建線程系統需要分配資源、終止線程系統需要回收資源,所以如果可以重用線程,則可以減去新建/終止的開銷以提升性能。同時,使用線程池的語法比自己新建線程執行線程更加簡潔。
Python 為我們提供了 ThreadPoolExecutor 來實現線程池,此線程池默認子線程守護。它的適應場景為突發性大量請求或需要大量線程完成任務,但實際任務處理時間較短。
其中 max_workers 為線程池中的線程個數,常用的遍歷方法有 map 和 submit+as_completed 。根據業務場景的不同,若我們需要輸出結果按遍歷順序返回,我們就用 map 方法,若想誰先完成就返回誰,我們就用 submit+as_complete 方法。
我們把一個時間段內只允許一個線程使用的資源稱為臨界資源,對臨界資源的訪問,必須互斥的進行。互斥,也稱間接制約關系。線程互斥指當一個線程訪問某臨界資源時,另一個想要訪問該臨界資源的線程必須等待。當前訪問臨界資源的線程訪問結束,釋放該資源之後,另一個線程才能去訪問臨界資源。鎖的功能就是實現線程互斥。
我把線程互斥比作廁所包間上大號的過程,因為包間里只有一個坑,所以只允許一個人進行大號。當第一個人要上廁所時,會將門上上鎖,這時如果第二個人也想大號,那就必須等第一個人上完,將鎖解開後才能進行,在這期間第二個人就只能在門外等著。這個過程與代碼中使用鎖的原理如出一轍,這里的坑就是臨界資源。 Python 的 threading 模塊引入了鎖。 threading 模塊提供了 Lock 類,它有如下方法加鎖和釋放鎖:
我們會發現這個程序只會列印「第一道鎖」,而且程序既沒有終止,也沒有繼續運行。這是因為 Lock 鎖在同一線程內第一次加鎖之後還沒有釋放時,就進行了第二次 acquire 請求,導致無法執行 release ,所以鎖永遠無法釋放,這就是死鎖。如果我們使用 RLock 就能正常運行,不會發生死鎖的狀態。
在主線程中定義 Lock 鎖,然後上鎖,再創建一個子 線程t 運行 main 函數釋放鎖,結果正常輸出,說明主線程上的鎖,可由子線程解鎖。
如果把上面的鎖改為 RLock 則報錯。在實際中設計程序時,我們會將每個功能分別封裝成一個函數,每個函數中都可能會有臨界區域,所以就需要用到 RLock 。
一句話總結就是 Lock 不能套娃, RLock 可以套娃; Lock 可以由其他線程中的鎖進行操作, RLock 只能由本線程進行操作。
⑥ 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()函數,兩者的主要區別在於: