python主線程子線程
1. 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 只能由本線程進行操作。
2. 關於python多線程的一些問題。
創建的子線程默認是非守護的。
非守護:當主線程結束時,子線程繼續運行,二者互不影響。
子線程是守護線程:當主線程結束時,子線程也結束(不管子線程工作有沒有完成)。
join作用是線程同步,是讓主線程等待子線程結束才結束(主線程完成工作了也不結束,阻塞等待,等子線程完成其工作才一起結束)。
相信此時你已經懂你的兩個問題了。
沒加join的時候主線程結束了,所以命令提示符>>>就出來了,可是子線程還沒結束,過了3/5秒後列印了字元串。加了join後主線程等兩個子線程都結束才一起結束,所以最後才出來>>>。
理解確實有點偏差。守護是指子線程守護著主線程,你死我也死,謂之守護。
3. 什麼是線程(多線程),Python多線程的好處
幾乎所有的操作系統都支持同時運行多個任務,一個任務通常就是一個程序,每一個運行中的程序就是一個進程。當一個程序運行時,內部可能包含多個順序執行流,每一個順序執行流就是一個線程。
線程和進程
幾乎所有的操作系統都支持進程的概念,所有運行中的任務通常對應一個進程(Process)。當一個程序進入內存運行時,即變成一個進程。進程是處於運行過程中的程序,並且具有一定的獨立功能。進程是系統進行資源分配和調度的一個獨立單位。
一般而言,進程包含如下三個特徵:
獨立性:進程是系統中獨立存在的實體,它可以擁有自己的獨立的資源,每一個進程都擁有自己的私有的地址空間。在沒有經過進程本身允許的情況下,一個用戶進程不可以直接訪問其他進程的地址空間。
動態性:進程與程序的區別在於,程序只是一個靜態的指令集合,而進程是一個正在系統中活動的指令集合。在進程中加入了時間的概念。進程具有自己的生命周期和各種不同的狀態,在程序中是沒有這些概念的。
並發性:多個進程可以在單個處理器上並發執行,多個進程之間不會互相影響。
並發(Concurrency)和並行(Parallel)是兩個概念,並行指在同一時刻有多條指令在多個處理器上同時執行;並發才旨在同一時刻只能有一條指令執行,但多個進程指令被快速輪換執行,使得在宏觀上具有多個進程同時執行的效果。
大部分操作系統都支持多進程並發執行,現代的操作系統幾乎都支持同時執行多個任務。例如,程序員一邊開著開發工具在寫程序,一邊開著參考手冊備查,同時還使用電腦播放音樂……除此之外,每台電腦運行時還有大量底層的支撐性程序在運行……這些進程看上去像是在同時工作。
但事實的真相是,對於一個 CPU 而言,在某個時間點它只能執行一個程序。也就是說,只能運行一個進程,CPU 不斷地在這些進程之間輪換執行。那麼,為什麼用戶感覺不到任何中斷呢?
這是因為相對人的感覺來說,CPU 的執行速度太快了(如果啟動的程序足夠多,則用戶依然可以感覺到程序的運行速度下降了)。所以,雖然 CPU 在多個進程之間輪換執行,但用戶感覺到好像有多個進程在同時執行。
現代的操作系統都支持多進程的並發執行,但在具體的實現細節上可能因為硬體和操作系統的不同而採用不同的策略。比較常用的策略有:
共用式的多任務操作策略,例如 Windows 3.1 和 Mac OS 9 操作系統採用這種策略;
搶占式的多任務操作策略,其效率更高,目前操作系統大多採用這種策略,例如 Windows NT、Windows 2000 以及 UNIX/Linux 等操作系統。
多線程則擴展了多進程的概念,使得同一個進程可以同時並發處理多個任務。線程(Thread)也被稱作輕量級進程(Lightweight Process),線程是進程的執行單元。就像進程在操作系統中的地位一樣,線程在程序中是獨立的、並發的執行流。
當進程被初始化後,主線程就被創建了。對於絕大多數的應用程序來說,通常僅要求有一個主線程,但也可以在進程內創建多個順序執行流,這些順序執行流就是線程,每一個線程都是獨立的。
線程是進程的組成部分,一個進程可以擁有多個線程,一個線程必須有一個父進程。線程可以擁有自己的堆棧、自己的程序計數器和自己的局部變數,但不擁有系統資源,它與父進程的其他線程共享該進程所擁有的全部資源。因為多個線程共享父進程里的全部資源,因此編程更加方便;但必須更加小心,因為需要確保線程不會妨礙同一進程中的其他線程。
線程可以完成一定的任務,可以與其他線程共享父進程中的共享變數及部分環境,相互之間協同未完成進程所要完成的任務。
線程是獨立運行的,它並不知道進程中是否還有其他線程存在。線程的運行是搶占式的,也就是說,當前運行的線程在任何時候都可能被掛起,以便另外一個線程可以運行。
一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以並發運行。
從邏輯的角度來看,多線程存在於一個應用程序中,讓一個應用程序可以有多個執行部分同時執行,但操作系統無須將多個線程看作多個獨立的應用,對多線程實現調度和管理,以及資源分配。線程的調度和管理由進程本身負責完成。
簡而言之,一個程序運行後至少有一個進程,在一個進程中可以包含多個線程,但至少要包含一個主線程。
歸納起來可以這樣說,操作系統可以同時執行多個任務,每一個任務就是一個進程,進程可以同時執行多個任務,每一個任務就是一個線程。
多線程的好處
線程在程序中是獨立的、並發的執行流。與分隔的進程相比,進程中線程之間的隔離程度要小,它們共享內存、文件句柄和其他進程應有的狀態
因為線程的劃分尺度小於進程,使得多線程程序的並發性高。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。
線程比進程具有更高的性能,這是由於同一個進程中的線程都有共性多個線程共享同一個進程的虛擬空間。線程共享的環境包括進程代碼段、進程的公有數據等,利用這些共享的數據,線程之間很容易實現通信。
操作系統在創建進程時,必須為該進程分配獨立的內存空間,並分配大量的相關資源,但創建線程則簡單得多。因此,使用多線程來實現並發比使用多進程的性能要高得多。
總結起來,使用多線程編程具有如下幾個優點:
進程之間不能共享內存,但線程之間共享內存非常容易。
操作系統在創建進程時,需要為該進程重新分配系統資源,但創建線程的代價則小得多。因此,使用多線程來實現多任務並發執行比使用多進程的效率高。
Python 語言內置了多線程功能支持,而不是單純地作為底層操作系統的調度方式,從而簡化了 Python 的多線程編程。
在實際應用中,多線程是非常有用的。比如一個瀏覽器必須能同時下載多張圖片;一個 Web 伺服器必須能同時響應多個用戶請求;圖形用戶界面(GUI)應用也需要啟動單獨的線程,從主機環境中收集用戶界面事件……總之,多線程在實際編程中的應用是非常廣泛的。
4. python中主線程怎樣捕獲子線程的異常
最近因為別的需求,寫了一個模塊,似乎在這里能用得上:
https://github.com/SakuraSa/ChatProcess
其中的 example.py :
#!/usr/bin/envpython
#coding=utf-8
"""
example
"""
__author__='Rnd495'
fromtimeimportsleep
fromChatProcessimportChatroom
classEcho(Chatroom):
"""
Echo
"""
defresponse(self,data):
ifdata.startswith('sleep'):
sec=float(data[6:])
sleep(sec)
return'wakeupafter%dms'%(sec*1000)
elifdata:
returndata
else:
self.stop()
return'goodbye'
if__name__=='__main__':
,ProcessError
print'process01:'
e=Echo.create_process(lifetime=1).start()
printe.chat('Helloworld!'),e.remain
printe.chat('sleep:0.1'),e.remain
printe.chat(''),e.remain
print''
print'process02:'
e=Echo.create_process(lifetime=1).start()
try:
printe.chat('Helloworld!'),e.remain
printe.chat('sleep:1.0'),e.remain
printe.chat(''),e.remain
exceptTimeoutError,error:
print'error:',error
print''
print'process03:'
e=Echo.create_process(lifetime=1).start()
try:
printe.chat('Helloworld!'),e.remain
printe.chat('sleep:notanum'),e.remain
printe.chat(''),e.remain
exceptProcessError,error:
print'error:',error
運行結果為:
process01:
Helloworld!0.773000001907
wakeupafter100ms0.549000024796
goodbye0.547000169754
process02:
Helloworld!0.868000030518
error:TimeoutError
process03:
Helloworld!0.868000030518
error:('Erroroccurredonloop',ValueError('couldnotconvertstringtofloat:notanum',))
在其中的 process01 中,主進程捕獲了 超時
在其中的 process02 中,主進程捕獲了 子進程的錯誤
不知道你能不能用得上
5. python子線程崩潰會影響主線程么
會的。
當一個進程啟動之後,會默認產生一個主線程,因為線程是程序執行的最小單位,當設置多線程時,主線程會創建多個子線程,在Python中,默認情況下就是setDaemon(False),主線程執行完自己的任務以後,就退出了,此時子線程會繼續執行自己的任務,直到自己的任務結束。
Python是一種廣泛使用的解釋型、高級和通用的編程語言。Python由荷蘭數學和計算機科學研究學會的GuidovanRossum創造,第一版發布於1991年,它是ABC語言的後繼者,也可以視之為一種使用傳統中綴表達式的LISP方言。Python提供了高效的高級數據結構,還能簡單有效地面向對象編程。