python的線程join
A. 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可以折中的利用計算機的多核:啟動八個進程,每個進程有一個線程。這樣就可以利用多進程解決多核問題。
B. join函數python
join函數python就是把一個list中所有的串按照你定義的分隔符連接起來。
join是string類型的一個函數,用調用他睜慶的字元串去連接參數里的列表,python裡面萬物皆對象,調用join函數,將後面的列表裡的值用逗號連接成新的字元串。str(i)foriinlist這是一個映射,就是把list中每個值都轉換成字元串。
含義
python中得thread的一些機制和C/C++不同:在C/C++中,主線程結束後,其子線程會默認被主線程kill掉。而在python中,主線程結束後,會默認等待子線程結束後,主線程才退出。
python對於thread的管理中有兩個函數:join和setDaemon。
join:如在一個線程B中調悉敬握用threada。join(),則threada結束後,線程B才會接著threada。join()往後運行。
setDaemon:主線程A啟動了子線程B,調用b。setDaemaon(True),則主線程結束時,稿鋒會把子線程B也殺死,與C/C++中得默認效果是一樣的。
C. Python threading 中join()的作用
Python中join()的作用:(菜鳥網路) join([time]): 等待至線程中止。這阻塞調用線程直至線程的join() 方法被調用中止-正常退出或者拋出未處理的異常-或者是可選的超時發生
看著定義大致明白,但是自己確不好理解。主要的功能就是多線程的線程獨占,讓此時只有一個線程運行。
1.子線程為什麼需要join?
join阻塞主線程,可以讓主線程獲得子線程的處理結果。
如果沒有join,由於子線程sleep,尚未append到tmp_list,例子中print tmp_list為空列表,join後即能在append執行後print出來。
如果不需要子線程的處理結果,那麼可以不join,當然join了也沒啥影響。
2.為什麼要寫成for循環join的形式?
這個在上文方式二中即提出了,可以即讓子線程非同步執行,又讓主線程等待結果。
D. 關於python多線程的一些問題。
創建的子線程默認是非守護的。
非守護:當主線程結束時,子線程繼續運行,二者互不影響。
子線程是守護線程:當主線程結束時,子線程也結束(不管子線程工作有沒有完成)。
join作用是線程同步,是讓主線程等待子線程結束才結束(主線程完成工作了也不結束,阻塞等待,等子線程完成其工作才一起結束)。
相信此時你已經懂你的兩個問題了。
沒加join的時候主線程結束了,所以命令提示符>>>就出來了,可是子線程還沒結束,過了3/5秒後列印了字元串。加了join後主線程等兩個子線程都結束才一起結束,所以最後才出來>>>。
理解確實有點偏差。守護是指子線程守護著主線程,你死我也死,謂之守護。
E. 小白都看懂了,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 核心數
F. Python中threading的join和setDaemon的區別及用法
Python多線程編程時經常會用到join()和setDaemon()方法,基本用法如下:
join([time]): 等待至線程中止。這阻塞調用線程直至線程的join() 方法被調用中止-正常退出或者拋出未處理的異常-或者是可選的超時發生。
setDaemon,將該線程標記為守護線程或用戶線程
1、join ()方法:主線程A中,創建了子線程B,並且在主線程A中調用了B.join(),那麼,主線程A會在調用的地方等待,直到子線程B完成操作後,才可以接著往下執行,那麼在調用這個線程時可以使用被調用線程的join方法。
原型:join([timeout]),裡面的參數時可選的,代表線程運行的最大時間,即如果超過這個時間,不管這個此線程有沒有執行完畢都會被回收,然後主線程或函數都會接著執行的。
import threadingimport time class MyThread(threading.Thread): def __init__(self, id): threading.Thread.__init__(self) self.id = id def run(self): x = 0 time.sleep(10) print(self.id) print('線程結束:'+str(time.time())) if __name__ == "__main__": t1 = MyThread(999) print('線程開始:'+str(time.time())) t1.start() print('主線程列印開始:'+str(time.time())) for i in range(5): print(i) time.sleep(2) print('主線程列印結束:' + str(time.time()))
線程開始:1497534590.2784667
主線程列印開始:1497534590.2794669
0
1
2
3
4
主線程列印結束:1497534592.279581
999
線程結束:1497534600.2800388
從列印結果可知,線程t1 start後,主線程並沒有等線程t1運行結束後再執行,而是在線程執行的同時,執行了後面的語句。
現在,把join()方法加到啟動線程後面(其他代碼不變)
import threadingimport time class MyThread(threading.Thread): def __init__(self, id): threading.Thread.__init__(self) self.id = id def run(self): x = 0 time.sleep(10) print(self.id) print('線程結束:'+str(time.time())) if __name__ == "__main__": t1 = MyThread(999) print('線程開始:'+str(time.time())) t1.start() t1.join() print('主線程列印開始:'+str(time.time())) for i in range(5): print(i) time.sleep(2) print('主線程列印結束:' + str(time.time()))
線程開始:1497535176.5019968
999
線程結束:1497535186.5025687
主線程列印開始:1497535186.5025687
0
1
2
3
4
主線程列印結束:1497535188.5026832
線程t1 start後,主線程停在了join()方法處,等子線程t1結束後,主線程繼續執行join後面的語句。
2、setDaemon()方法。主線程A中,創建了子線程B,並且在主線程A中調用了B.setDaemon(),這個的意思是,把主線程A設置為守護線程,這時候,要是主線程A執行結束了,就不管子線程B是否完成,一並和主線程A退出.這就是setDaemon方法的含義,這基本和join是相反的。此外,還有個要特別注意的:必須在start() 方法調用之前設置。import threading
import time class MyThread(threading.Thread): def __init__(self, id): threading.Thread.__init__(self) self.id = id def run(self): x = 0 time.sleep(10) print(self.id) print("This is:" + self.getName()) # 獲取線程名稱 print('線程結束:' + str(time.time())) if __name__ == "__main__": t1 = MyThread(999) print('線程開始:'+str(time.time())) t1.setDaemon(True) t1.start() print('主線程列印開始:'+str(time.time())) for i in range(5): print(i) time.sleep(2) print('主線程列印結束:' + str(time.time()))
線程開始:1497536678.8509264
主線程列印開始:1497536678.8509264
0
1
2
3
4
主線程列印結束:1497536680.8510408
t1.setDaemon(True)的操作,將子線程設置為了守護線程。根據setDaemon()方法的含義,父線程列印內容後便結束了,不管子線程是否執行完畢了。
如果在線程啟動前沒有加t1.setDaemon(True),輸出結果為:
線程開始:1497536865.3215919
主線程列印開始:1497536865.3215919
0
1
2
3
4
主線程列印結束:1497536867.3217063
999
This is:Thread-1
線程結束:1497536875.3221638
程序運行中,執行一個主線程,如果主線程又創建一個子線程,主線程和子線程就分兵兩路,分別運行,那麼當主線程完成想退出時,會檢驗子線程是否完成,如果子線程未完成,則主線程會等待子線程完成後再退出;
有時我們需要的是,子線程運行完,才繼續運行主線程,這時就可以用join方法(在線程啟動後面);
但是有時候我們需要的是,只要主線程完成了,不管子線程是否完成,都要和主線程一起退出,這時就可以用setDaemon方法(在線程啟動前面)。
G. python中的進程-實戰部分
如果想了解進程 可以先看一下這一篇 python中的進程-理論部分
python中的多線程無法利用多核優勢,如果想要充分地使用多核CPU的資源(os.cpu_count()查看),在python中大部分情況需要使用多進程。Python提供了multiprocessing。
multiprocessing模塊用來開啟子進程,並在子進程中執行我們定製的任務(比如函數),該模塊與多線程模塊threading的編程介面類似。
multiprocessing模塊的功能眾多:支持子進程、通信和共享數據、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。
需要再次強調的一點是:與線程不同,進程沒有任何共享狀態,進程修改的數據,改動僅限於該進程內。
創建進程的類 :
參數介紹:
group參數未使用,值始終為None
target表示調用對象,即子進程要執行的任務
args表示調用對象的位置參數元組,args=(1,2,'tiga',)
kwargs表示調用對象的字典,kwargs={'name':'tiga','age':18}
name為子進程的名稱
方法介紹:
p.start():啟動進程,並調用該子進程中的p.run()
p.run():進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法
p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵屍進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那麼也將不會被釋放,進而導致死鎖
p.is_alive():如果p仍然運行,返回True
p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的進程,而不能join住run開啟的進程
屬性介紹:
注意:在windows中Process()必須放到# if __name__ == '__main__':下
創建並開啟子進程的兩種方式
方法一:
方法二:
有了join,程序不就是串列了嗎???
terminate與is_alive
name與pid
H. python 怎麼實現多線程的
線程也就是輕量級的進程,多線程允許一次執行多個線程,Python是多線程語言,它有一個多線程包,GIL也就是全局解釋器鎖,以確保一次執行單個線程,一個線程保存GIL並在將其傳遞給下一個線程之前執行一些操作,也就產生了並行執行的錯覺。