10月13日学习内容整理:线程,创建线程(threading模块),守护线程,GIL(全局解释器互斥锁)...
一、線程
1、概念:一條流水線的工作過程
2、和進程的區(qū)別和關(guān)系
(1)關(guān)系
》進程是資源單位,線程是執(zhí)行單位,cpu真正執(zhí)行的是線程
》一個進程至少有一個線程
》多線程針對的是一個進程的概念
》從執(zhí)行的角度說:執(zhí)行一個進程就相當于開啟一個控制(主)線程
》從資源的角度說:開啟一個進程就是開辟一個內(nèi)存空間
(2)區(qū)別
》核心1:創(chuàng)建線程開銷很小,不用申請空間(比進程快10-100倍);而進程開銷大需要申請空間
》核心2:同一進程的多個線程共享該進程的資源和地址空間;但是不同進程的地址空間是隔離的
》線程可以直接訪問進程的數(shù)據(jù);而子進程是會拷貝父進程的所有數(shù)據(jù)
》同一進程的多個線程之間可以直接通信;而不同的進程通信則要依靠IPC機制
》線程可以控制同一進程的其它線程;進程只能控制它的子進程
》改變主線程可能貴影響其它線程的行為;改變父進程不會影響子進程
3、和進程的對比
》線程沒有“父子”概念
》同一進程的不同線程的進程ID都是一樣的
》由于線程共享進程的資源,所以當線程對數(shù)據(jù)修改時修改的就是該進程內(nèi)存空間中的數(shù)據(jù),但要注意線程和控制線程(該進程)對數(shù)據(jù)處理的時間先后順序
?
二、創(chuàng)建線程
1、代碼實現(xiàn):Thread類(threading模塊)
t = Thread(target=函數(shù)名,args=(參數(shù),)/kwargs={字典}) ? 實例化
t.start() ? 發(fā)送創(chuàng)建線程請求(由于創(chuàng)建線程開銷很小,所以發(fā)送請求的同時基本就開啟了線程)
t .join() ? ? 主線程等待線程結(jié)束
from threading import Thread from multiprocessing import Processdef work(n):print('%s is running' %n)if __name__ == '__main__':t=Thread(target=work,args=(1,))t.start()print('主線程')?
2、其它方法:
(1)對象方法
》isAlive() 線程是否存活
》getName() ?獲取當前線程的名字,默認Thread-1,Thread-2,...這樣
》setName() ? 設(shè)置當前線程的名字
(2)模塊提供的方法
》currentThread() 獲取當前線程對象
》enumerate() 顯示當前活躍線程,輸出列表
》activeCount() ?當前活躍線程的數(shù)目
?
三、守護線程
1、代碼實現(xiàn):
t.daemon=True ? ?必須在start()之前設(shè)置
2、概念
》當控制線程(從執(zhí)行角度上看是該進程)結(jié)束守護線程也隨之結(jié)束
》控制線程只有當所有非守護線程都結(jié)束時才算結(jié)束
from threading import Thread import time def foo():print(123)time.sleep(5)print("end123")def bar():print(456)time.sleep(3)print("end456")if __name__ == '__main__':t1=Thread(target=foo)t2=Thread(target=bar)t1.daemon=Truet1.start()t2.start()print("main-------")?
四、GIL:全局解釋器互斥鎖(不是python的特性)
1、產(chǎn)生背景:在Cpython解釋器中特有的,因為python解釋器帶有數(shù)據(jù)回收機制,這樣就會帶來回收和利用的沖突性,所以就要求一個進程的多個線程同一時間只能有一個被執(zhí)行
2、本質(zhì):就是一把互斥鎖,實現(xiàn)將并發(fā)變?yōu)榇?#xff0c;保證數(shù)據(jù)安全
》》》#1 所有數(shù)據(jù)都是共享的,這其中,代碼作為一種數(shù)據(jù)也是被所有線程共享的(test.py的所有代碼以及Cpython解釋器的所有代碼)
》》》例如:test.py定義一個函數(shù)work,在進程內(nèi)所有線程都能訪問到work的代碼,于是我們可以開啟三個線程然后target都指向該代碼,能訪問到意味著就是可以執(zhí)行。
》》》#2 所有線程的任務(wù),都需要將任務(wù)的代碼當做參數(shù)傳給解釋器的代碼去執(zhí)行,即所有的線程要想運行自己的任務(wù),首先需要解決的是能夠訪問到解釋器的代碼。
?
在一個python的進程內(nèi),不僅有test.py的主線程或者由該主線程開啟的其他線程,還有解釋器開啟的垃圾回收等解釋器級別的線程,總之,所有線程都運行在這一個進程內(nèi),毫無疑問
?
3、實現(xiàn)方式:
with ?lock:
python解釋器的代碼()
這個lock就是GIL
?
?
4、GIL和lock:
》要根據(jù)不同的數(shù)據(jù)來加不同的鎖,因為GIL保證不了用戶數(shù)據(jù)的安全,GIL只能保護python解釋器級別的數(shù)據(jù)
》線程要想被執(zhí)行就要去搶GIL鎖(也就是爭執(zhí)行權(quán)限)
?
5、GIL和多線程
》對于多個計算密集型的程序
(1)單核情況下:因為始終只有一個cpu去執(zhí)行計算,所以就要求開銷要小,故選擇多線程
(2)多核情況下:因為有多個cpu去執(zhí)行計算,能夠?qū)崿F(xiàn)真正的并行,效率會很高,而線程每次只有一個在執(zhí)行沒有利用多核的優(yōu)勢,故選擇多進程
》對于多個IO密集型程序
(1)單核情況下:因為只有一個cpu執(zhí)行,需要不斷的切換來實現(xiàn)并發(fā),所以要求開銷盡量要小,故選擇多線程
(2)多核情況下:多個cpu執(zhí)行的話,遇到IO仍然要阻塞,若是多進程的話就會常常是阻塞狀態(tài),運行時間就會是時間最長的哪個程序,而如果是多線程,首先開銷很小,其次每次執(zhí)行一個線程遇到IO阻塞就會立即去處理別的線程,這樣效率會提高,故選擇多線程
?
6、為什么要有GIL和代碼運行的整個過程
》首先,python解釋器帶有的數(shù)據(jù)回收機制,本身是有計數(shù)器的,通過計數(shù)來判斷是否為垃圾數(shù)據(jù),而這個計數(shù)就是python解釋器中的代碼提供的功能(其實也是一段程序來實現(xiàn))也就是python解釋器級別的數(shù)據(jù),而肯定有可能會出現(xiàn)當python解釋器要回收某一個數(shù)據(jù)而某一個程序正好要使用這個數(shù)據(jù)的情況,這就帶來了沖突,為了解決這種沖突保護數(shù)據(jù)安全就必須保證同一時間內(nèi)只能有一個程序來使用python解釋器的代碼,這就變成了串行,所以就引入了GIL鎖的應(yīng)用
》我們寫一段python代碼,首先肯定要打開python解釋器,這就相當于開啟了一個代碼解釋器的進程,我們在解釋器中寫代碼其實就是用了解釋器的功能(也就是通過一段程序?qū)崿F(xiàn)的功能),而代碼本身是不會自己運行的,運行我們所寫的代碼其實就是開啟了另一個進程(即是python解釋器進程的子進程)也就是將我們所寫的代碼作為參數(shù)傳給python解釋器來執(zhí)行(這其實就是相當于子進程創(chuàng)建時會把父進程(python解釋器的代碼)的所有數(shù)據(jù)都拷貝,這樣子進程(我們所寫的代碼)才能運行),而由于GIL鎖的存在,每個程序要想運行就要去搶這把鎖也就是爭取運行權(quán)限,爭到了就執(zhí)行若遇到阻塞操作系統(tǒng)就會強制釋放掉該程序的運行權(quán)限(也就是釋放掉GIL鎖),這樣別的程序再去搶然后運行,等之前的程序處于就緒態(tài)時會再去搶GIL鎖接著之前的運行狀態(tài)繼續(xù)執(zhí)行,這就是一段代碼要想運行起來必須經(jīng)歷的過程
補充一下:
1、開啟python解釋器操作系統(tǒng)其實是將解釋器的代碼從硬盤中取出放到內(nèi)存中去運行,我們所寫的代碼也是這樣的
2、其實進程和線程的創(chuàng)建和開啟(個數(shù)快慢等,當然線程的開銷肯定要比進程小很多),包括程序運行時對GIL鎖的爭搶和釋放以及運行狀態(tài)的保留等,這些都是由操作系統(tǒng)及電腦性能來控制和決定的,無法由程序本身來操控
》上面說到每個程序運行前要搶GIL鎖(運行權(quán)限),運行起來時就是按照自己的代碼來運行,會產(chǎn)生自己的數(shù)據(jù),而這個數(shù)據(jù)GIL是保護不了的,因為GIL只是鎖住了解釋器而沒有鎖住每個進程對同一塊數(shù)據(jù)的處理功能,所以這就是為什么不同的數(shù)據(jù)要加不同的鎖來保護,GIL只是保護python解釋器代碼級別的數(shù)據(jù)而已
?
轉(zhuǎn)載于:https://www.cnblogs.com/wanghl1011/articles/7663035.html
總結(jié)
以上是生活随笔為你收集整理的10月13日学习内容整理:线程,创建线程(threading模块),守护线程,GIL(全局解释器互斥锁)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [js插件开发教程]一步步开发一个可以定
- 下一篇: UVA 1602 Lattice Ani