多進程:
多任務(wù):
簡單的說就是操作系統(tǒng)(os)可以同時運行多個任務(wù)休弃。我們的操作系統(tǒng)就是多任務(wù)玫芦。
單核cpu:
? ? ? ? ? ? ? ? ? ? ? 1桥帆、時間片輪換 :每個進程被分配一個時間? ? ? ? ? ?段慎皱,稱作它的時間片茫多,即該進程允許運行的時間
? ? ? ? ? ? ? ? ? ? 2、優(yōu)先級別調(diào)度:優(yōu)先級別越高就約會被優(yōu)先調(diào)用
多核cpu:??
1跪帝、并發(fā)? ?:
并發(fā)就是只有一個CPU資源伞剑,程序(或線程)之間要競爭得到執(zhí)行機會黎泣。第一個階段缤谎,在A執(zhí)行的過程中B坷澡,C不會執(zhí)? 行洋访,因為這段時間內(nèi)這個CPU資源被A競爭到了,同理呆抑,第二個階段只有B在執(zhí)行鹊碍,第三個階段只有C在執(zhí)行侈咕。其實耀销,并發(fā)過程? ? ?中熊尉,A掌腰, B齿梁,C并不是同時在進行的(微觀角度)。但又是同時進行的(宏觀角度)伦忠。
? ? ? ? ? ? ? ? ? ? ? ? ?微觀角度:微觀角度:所有的并發(fā)處理都有排隊等候缓苛,喚醒未桥,執(zhí)行等這樣的步驟冬耿,在微觀上他們都是序列被處理的萌壳,如果是同一時? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 刻到達的請求(或線程)也會根據(jù)優(yōu)先級的不同袱瓮,而先后進入隊列排隊等候執(zhí)行尺借。
? ? ? ? ? ? ? ? ? ? ? ? ? ?宏觀角度:多個幾乎同時到達的請求(或線程)在宏觀上看就像是同時在被處理
2燎斩、并行? :
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?指兩個或兩個以上事件(或線程)在同一時刻發(fā)生栅表,是真正意義上的不同事件或線程在同一時刻怪瓶,在不同CPU資源呢上? (多? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????核)洗贰,同時執(zhí)行哆姻。
? 簡易理解:
????????????????????????你在敲代碼矛缨,煙癮犯了箕昭,你一直到敲完了以后才去抽煙,這就說明你不支持并發(fā)也不支持并行泌霍。
? ? ? ? ? ? ? ? ? ? ????你敲代碼敲到一半朱转,煙癮犯了藤为,你停了下來抽空了根煙缅疟,抽完繼續(xù)敲代碼存淫,這說明你支持并發(fā)桅咆。
? ? ? ? ? ? ? ? ? ? ?????你敲代碼敲到一半轧邪,煙癮犯了忌愚,你一邊敲一邊抽煙硕糊,這說明你支持并行简十。
Fork子進程
程序:
? ??????????????編寫完的代碼螟蝙,在沒有運行的情況下民傻,稱之為程序场斑。
進程:
????????????????正在運行的程序稱為進程漏隐。? ? ? ? ?進程,除了包含代碼外取具,還需要運行環(huán)境等暇检,所以和程序是存在區(qū)別的占哟。
Fork:
????????????????python在os模塊中封裝了常用的系統(tǒng)調(diào)用榨乎,其中就有fork蜜暑。注意:fork只能在linux和mac中運行不能在windows中運行肛捍。
Python中的fork() 函數(shù)可以獲得系統(tǒng)中進程的PID ( Process ID )拙毫,返回0則為子進程缀蹄,否則就是父進程缺前,然后可以據(jù)此對運行中的進程進行操作衅码。
運行結(jié)果如下圖:
getpid垛玻、getppid:
os.getpid:獲得子進程編號
getppid:獲得父進程編號
運行結(jié)果如下圖所示(會無限循環(huán)下去因為用的是while True死循環(huán)):
多進程修改全局變量
其運行結(jié)果如下圖:
????????????????正常情況下后一個輸出結(jié)果應(yīng)該位1+1=2的巫糙,但是結(jié)果中顯示都是位1参淹,說明了進程間是無法共享數(shù)據(jù)的浙值。
????????????注意:多個進程間开呐,每個進程的所有數(shù)據(jù)(包括全局變量)都是各自擁有一份的,互不影響卵惦。
多個fork問題
同時進行多個fork
其結(jié)果如下圖:
出現(xiàn)上圖結(jié)果的原因入下圖所示:
windows下實現(xiàn)多進程:multiprocessing
其運行結(jié)果如下圖:
????????????????從上面的運行結(jié)果中可以看出 主程序比子程序運行結(jié)束的要快,那么就需要用t1.join來進行約束(表示這個子進程運行完成才能進行主進程)
在上面的程序中是帶參數(shù)的啡捶,如果方法中不帶參數(shù) 則可以直接將參數(shù)去掉届慈。
類實現(xiàn)方式:
????????????????進程類的實現(xiàn)非常的簡單金顿,只要繼承了Process類就ok了鲤桥,重寫該類的run方法茶凳,run方法里面的代碼,就是我們需要的子進程代碼猪狈。
代碼如下圖:
運行結(jié)果如下
????????????????在進程類的實現(xiàn)中如果想要初始化一些前面我們提到過的參數(shù),如進程名稱等疆前,可以使用__init__借助父類來完成竹椒。(注意第一個屬性名稱具有特殊意義胸完,如果不用super().__init__將會無法進行修改)如下圖所示:
結(jié)果如下圖:
這時候的函數(shù)名字就是graves
阻塞/非阻塞、同步/異步:
????????????????在進行網(wǎng)絡(luò)變成時誓琼,長用到同步(Sync)/異步(Async)腹侣,阻塞(Block)/非阻塞(Unblock)四種調(diào)用方式:
同步:
????????????????所謂同步傲隶,就是在c端發(fā)出一個功能調(diào)用時跺株,在沒有得到結(jié)果之前乒省,該調(diào)用就不返回袖扛。也就是必須一件一件事做蛆封,等前一件做完了才能做下一件事惨篱。
異步:
????????????????當c端一個異步過程調(diào)用通過發(fā)出后砸讳,調(diào)用者不能立刻得到結(jié)果绣夺。實際處理這個調(diào)用的不見在完成后陶耍,通過狀態(tài)烈钞、通知和回調(diào)來通知調(diào)用者毯欣。
阻塞:
????????????????阻塞調(diào)用時值調(diào)用結(jié)果返回之前酗钞,當前線程會被掛起(線程進入非可執(zhí)行狀態(tài)砚作,在這個狀態(tài)下葫录,cpu不會給線程分配時間片米同,即線程暫停運行)面粮。函數(shù)只有在得到結(jié)果之后才會返回但金。
非阻塞:
????????????????非阻塞和阻塞的概念相對應(yīng)钱磅,指在不能立刻得到結(jié)果之前盖淡,該函數(shù)不會阻塞當前線程褪迟,而會立刻返回味赃。
進程池:
????????????????當我們需要的進程數(shù)量不多的時候心俗,我們可以使用multiprocessing的Process類來創(chuàng)建進程城榛。但是如果我們需要的進程特別多的時候狠持,手動創(chuàng)建工作量太大了喘垂,所以Python也為我們提供了Pool(池)的方式來創(chuàng)建大量進程王污。
其代碼入下圖所示:
運行結(jié)果如下圖所示:
注意:一般我們使用apply_async這個方法,表示非阻塞的運行阱驾,一旦使用了apply方法表示阻塞式執(zhí)行任務(wù),此時就是單任務(wù)執(zhí)行了(一般不會使用喧枷,特殊場景才會使用)
本地進程間的通信方式:
隊列(Queue)隧甚、管道(Pipe)戚扳、管理者(Manager)
隊列(queue)常用方法:
????????????Queue.qsize(): 返回queue中item的數(shù)量帽借,注意這個數(shù)量并不準確
????????????Queue.empty(): 如果隊列為空則返回True
????????????Queue.full():如果隊列充滿則返回True
????????????Queue.put(item[, block[, timeout]]): block表示是否阻塞砍艾,默認為True即阻塞凝垛,如果設(shè)定了timeout苔严,則阻塞timeout時長,如果仍然沒有空余的slot覆旭,則raise Queue.full exception型将。如果block=False七兜,那么就不阻塞腕铸,當queue full時,直接報Queue.full exception涛菠。寫入隊列對象
????????????Queue.get([block[, timeout]]) 獲得隊列對象
具體用法入下圖所示:
其運行結(jié)果為:
管道(Pipe):
????????????????????管道是一種兩個進程間進行單向通信的機制礁叔。因為管道傳遞數(shù)據(jù)的單向性言疗,管道又稱為半雙工管道晴圾。管道的這一特點決定了器使用的局限性。管道是Linux支持的最初Unix IPC形式之一噪奄,具有以下特點:
????? ????????????數(shù)據(jù)只能由一個進程流向另一個進程(其中一個讀管道死姚,一個寫管道);如果要進行雙工通信勤篮,需要建 立兩個管道都毒。
????? ????????????管道只能用于父子進程或者兄弟進程間通信碰缔。账劲,也就是說管道只能用于具有親緣關(guān)系的進程間通信。
用法與queue相似:
其運行結(jié)果為:
Manager:
????????????????Manager對象類似于服務(wù)器與客戶之間的通信金抡,做到了進程之間的數(shù)據(jù)共享瀑焦。Managers支持的數(shù)據(jù)類型有l(wèi)ist、dict梗肝、Namespace榛瓮、Lock、rlock巫击、Queue禀晓、Value、Arrayand so?on坝锰。
結(jié)果如下:
從圖中可以看出 manager做到了進程之間的數(shù)據(jù)共享
線程:
進程:
????????????????能夠完成多個任務(wù)粹懒,一般而言,一個進程就是一個獨立的軟件顷级,如我們在電腦上運行了多個QQ凫乖。進程(Process)是計算機中的程序關(guān)于某數(shù)據(jù)集合上的一次運行活動,是系統(tǒng)進行資源分配和調(diào)度的基本單位弓颈,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)帽芽。
線程:
? ? ? ? ? ? ? ?能夠完成多個任務(wù),一般而言恨豁,一個進程至少存在一個線程或者多個線程嚣镜,如打開網(wǎng)頁,啟動多個頁面選項卡橘蜜。線程菊匿,有時被稱為輕量級? ? ? ? ? ? ? ? ?進程(Lightweight Process付呕,LWP),是程序執(zhí)行流的最小單元跌捆。
注意:進程是程序的基本單位徽职,線程是程序的最小單位。類似于人們幣的基本單位是元佩厚,最小單位是分
兩者區(qū)別:
????????????1姆钉、一個程序中至少有一個進程,一個進程中至少有一個線程抄瓦;
????????????2潮瓶、線程的劃分尺度小于進程(占有資源),使得多線程程序的并發(fā)性高钙姊;
????????????3毯辅、進程運行過程中擁有獨立的內(nèi)存空間,而線程之間共享內(nèi)存煞额,從而極大的提高了程序的運行效率
????????????4思恐、線程不能獨立運行,必須存在于進程中
? ? ? ? ? ?5膊毁、多線程和多進程的寫法很像胀莹,其次就是多線程的運行速度很快如果沒有join或者設(shè)置為守護線程,主線程會直接執(zhí)行后面的代碼婚温,之后掛起描焰,等待所有的子線程運行完成后結(jié)束,才能結(jié)束缭召。
線程優(yōu)缺點:線程開銷小栈顷,但是不利于資源的管理和保護
進程與之相反:進程開銷大逆日,但是利于資源的管理和保護嵌巷,安全比較有保障
多線程------threading:
????????????????????????????Python3中取消了thread模塊,只有threading模塊室抽,所以我們使用threading模塊來學習多線程編程搪哪。
其運行結(jié)果入下圖:
重寫run方法:
結(jié)果如下:
傳遞參數(shù)版:
1、 線程同樣會有名稱
2坪圾、 當run運行完成晓折,該線程就會自動結(jié)束
3、 我們無法控制線程兽泄,但是可以通過一些別的方式來影響線程調(diào)度
4漓概、 下圖是線程的幾種狀態(tài)
===================================================================================================
Python的GIL(Global?Interpreter Lock):
官方定義:
? ??????????????In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
????????????????定義說,GIL是多線程間的一把互斥鎖病梢,并且是一把全局鎖胃珍,它保證了Cpython在內(nèi)存管理上面是線程安全的梁肿。 這里要注意一點的是,官方定義開頭就給了限制范圍觅彰,是在CPython這個解釋器下吩蔑。我們知道,python存在各種各樣的解釋器填抬,也就是說烛芬,在某些解釋器下,其實并不存在GIL這個東西飒责,經(jīng)過查閱資料赘娄,像JPython中就不存在GIL。
? ? ? ? ? ? ? ?結(jié)論:在Cpython解釋器中宏蛉,同一個進程下開啟的多線程擅憔,同一時刻只能有一個線程執(zhí)行,無法利用多核優(yōu)勢檐晕。GIL并不是Python的特性暑诸,Python完全可以不依賴于GIL。
多線程-共享全局變量?:
結(jié)果
????????????????????在圖中可以看到兩個子線程是可以共享主線程的數(shù)據(jù)的辟灰。此時的數(shù)據(jù)較小个榕,但是如果數(shù)據(jù)很大的時候就會可能出現(xiàn)別的問題了:
????????????????????假設(shè)兩個線程t1和t2都要對No=0進?增1運算,t1和t2都各對No修改100萬次次芥喇,No的最終的結(jié)果應(yīng)該為2000000西采。但是由于是多線程訪問,有可能出現(xiàn)下?情況:在No=0時继控,t1取得No=0械馆。此時系統(tǒng)把t1調(diào)度為”sleeping”狀態(tài),把t2轉(zhuǎn)換為”running”狀態(tài)武通,t2也獲得No=0霹崎。然后t2對得到的值進?加1并賦給No,使得No=1冶忱。然后系統(tǒng)?把t2調(diào)度為”sleeping”尾菇,把t1轉(zhuǎn)為”running”。線程t1?把它之前得到的0加1后賦值給No囚枪。這樣派诬,明明t1和t2都完成了1次加1?作,但結(jié)果仍然是No=1链沼。如圖:
其結(jié)果為:
如果我們將代碼中的time.sleep(3)注釋去掉
????????????????問題產(chǎn)?的原因就是沒有控制多個線程對同?資源的訪問默赂,對數(shù)據(jù)造成破壞,使得線程運?的結(jié)果不可預(yù)期括勺。這種現(xiàn)象稱為“非線程安全”缆八。那么該怎么解決著這個問題呢谒臼?
????????????????系統(tǒng)調(diào)?t1,然后獲取到No的值為0耀里,此時上?把鎖蜈缤,即不允許其他現(xiàn)在操作No對No的值進?+1解鎖,此時No的值為1冯挎,其他的線程就可以使?No了逗扒,?且是No的值不是0?是1同理其他線程在對No進?修改時啡直,都要先上鎖颗品,處理完后再解鎖朽缎,在上鎖的整個過程中不允許其他線程訪問,就保證了數(shù)據(jù)的正確性翰守。通俗點就是:A去上廁所孵奶,進去后把廁所門鎖上了,B就不能進去蜡峰,要等到A出來將鎖打開了才能進去了袁。所以上述問題就得需要一把“鎖”來解決----互斥鎖
互斥鎖:
????????????????當多個線程?乎同時修改某?個共享數(shù)據(jù)的時候,需要進?同步控制線程同步能夠保證多個線程安全訪問競爭資源湿颅,最簡單的同步機制是引?互斥鎖载绿。
? ? ? ? ? ? ? ? 某個線程要更改共享數(shù)據(jù)時,先將其鎖定油航,此時資源的狀態(tài)為“鎖定”崭庸,其他線程不能更改;直到該線程釋放資源谊囚,將資源的狀態(tài)變成“?鎖定”怕享,其他的線程才能再次鎖定該資源×ぃ互斥鎖保證了每次只有?個線程進?寫?操作函筋,從?保證了多線程情況下數(shù)據(jù)的正確性。
threading模塊中定義了Lock類余境,常用方法有:
#創(chuàng)建鎖:mutex = threading.Lock()
#鎖定:mutex.acquire([blocking])
#釋放:mutex.release()
? ??????????????鎖定?法acquire可以有?個blocking參數(shù)驻呐。如果設(shè)定blocking為True灌诅,則當前線程會堵塞芳来,直到獲取到這個鎖為?(如果沒有指定,那么默認為True)猜拾,如果設(shè)定blocking為False即舌,則當前線程不會堵塞。
鎖的好處:
????????????????確保了某段關(guān)鍵代碼只能由?個線程從頭到尾完整地執(zhí)?
鎖的壞處:????????????????
? ? ? ? ? ? ? ? ? ?阻?了多線程并發(fā)執(zhí)?挎袜,包含鎖的某段代碼實際上只能以單線程模式執(zhí)?顽聂,效 率就??地下降了肥惭。由于可以存在多個鎖,不同的線程持有不同的鎖紊搪,并試圖獲取對?持有的鎖時蜜葱,可能會造成死鎖。
具體做法看圖說話:
結(jié)果如下:
?共享數(shù)據(jù):
結(jié)果如下:
非共享數(shù)據(jù)無需加鎖耀石,因為局部變量不是共享的牵囤,不能彼此影響。
在多線程開發(fā)中滞伟,全局變量是多個線程都共享的數(shù)據(jù)揭鳞,?局部變量等是各?線程的,是?共享的梆奈。所以不存在非線程安全問題野崇。