進(jìn)程、線程、協(xié)程
一、什么是進(jìn)程
進(jìn)程是計算機中的程序關(guān)于某數(shù)據(jù)集合的一次運行活動,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。
在早期面向進(jìn)程設(shè)計的計算機結(jié)構(gòu)中,進(jìn)程是程序的基本執(zhí)行實體;在當(dāng)代面向進(jìn)程設(shè)計的計算機結(jié)構(gòu)中,進(jìn)程是線程的容器。
程序是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實體。
狹義定義:進(jìn)程是正在運行的程序的實例。
廣義定義∶進(jìn)程是一個具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運行活動。它是操作系統(tǒng)動態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。
注意:同一個程序執(zhí)行兩次,就會在操作系統(tǒng)中出現(xiàn)兩個進(jìn)程,所以我們可以同時運行一個軟件,分別做不同的事情也不會混亂。
每個進(jìn)程都有自己獨立的一塊內(nèi)存空間,一個進(jìn)程可以有多個線程,比如在Windows系統(tǒng)中,一個運行的xx.exe就是一個進(jìn)程。
二、什么是線程
進(jìn)程中的一個執(zhí)行任務(wù)(控制單元),負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)行。一個進(jìn)程至少有一個線程,一個進(jìn)程可以運行多個線程,多個線程可共享數(shù)據(jù)。
與進(jìn)程不同的是同類的多個線程共享進(jìn)程的堆和方法區(qū)資源,但每個線程有自己的程序計數(shù)器、虛擬機棧和本地方法棧,所以系統(tǒng)在產(chǎn)生一個線程,或是在各個線程之間作切換工作時,負(fù)擔(dān)要比進(jìn)程小得多,也正因為如此,線程也被稱為輕量級進(jìn)程。
三、進(jìn)程和線程的區(qū)別
根本區(qū)別:進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是處理器任務(wù)調(diào)度和執(zhí)行的基本單位;
內(nèi)存分配:
同一進(jìn)程的線程共享本進(jìn)程的地址空間和資源,而進(jìn)程之間的地址空間和資源是相互獨立的;
影響關(guān)系:
一個進(jìn)程崩潰后,在保護(hù)模式下不會對其他進(jìn)程產(chǎn)生影響,但是一個線程崩潰整個進(jìn)程都死掉。所以多進(jìn)程要比多線程健壯。
四、協(xié)程
協(xié)程(Coroutines)是一種比線程更加輕量級的存在。協(xié)程完全由程序所控制(在用戶態(tài)執(zhí)行),帶來的好處是性能大幅度的提升。
一個操作系統(tǒng)中可以有多個進(jìn)程;一個進(jìn)程可以有多個線程;同理,一個線程可以有多個協(xié)程。
協(xié)程是一個特殊的函數(shù),這個函數(shù)可以在某個地方掛起,并且可以重新在掛起處繼續(xù)運行。一個線程內(nèi)的多個協(xié)程的運行是串行的,這點和多進(jìn)程(多線程)在多核CPU上執(zhí)行時是不同的。多進(jìn)程(多線程)在多核CPU上是可以并行的。當(dāng)線程內(nèi)的某一個協(xié)程運行時,其它協(xié)程必須掛起。
進(jìn)程微觀
一、進(jìn)程調(diào)度
要想多個進(jìn)程交替運行,操作系統(tǒng)必須對這些進(jìn)程進(jìn)行調(diào)度,這個調(diào)度也不是隨機進(jìn)行的,而是需要遵循一定的法則,由此就有了進(jìn)程的調(diào)度算法。
先來先服務(wù)調(diào)度算法(先來后到)
短作業(yè)優(yōu)先調(diào)度算法(進(jìn)程的復(fù)雜程度不同)
時間片輪轉(zhuǎn)法(分配時間片)
多級反饋隊列(分級)
二、進(jìn)程的并行和并發(fā)
a.并行:并行是指兩者同時執(zhí)行,比如賽跑,兩個人都在不停的往前跑;i、(資源夠用,比如三個進(jìn)程,四核的cpu)
b.并發(fā):并發(fā)是指資源有限的情況下,兩者交替輪流使用資源,比如一座橋(單核cpu)同時只能過一個人,A走一段后,讓給B,B用完繼續(xù)給A,交替使用,目的是提高效率。
c.區(qū)別:i.并行是從微觀上,也就是在一個精確的時間片刻,有不同的程序在執(zhí)行,這就要求必須有多個處理器。ii.并發(fā)是從宏觀上,在一個時間段上可以看出是同時執(zhí)行的,比如一個服務(wù)器同時處理多個請求。
三、進(jìn)程的狀態(tài)
時間片即CPU分配給各個程序的時間,每個線程被分配一個時間段,稱作它的時間片,即該進(jìn)程允許運行的時間,使各個程序從表面上看是同時進(jìn)行的。
運行程序會創(chuàng)建進(jìn)程,然后將進(jìn)程提交到操作系統(tǒng),操作系統(tǒng)進(jìn)行進(jìn)程調(diào)度,此時就會進(jìn)入就緒、運行狀態(tài)(時間片輪轉(zhuǎn)會導(dǎo)致兩種狀態(tài)相互切換),如果在運行中遇到阻塞事件就會停滯進(jìn)入阻塞狀態(tài)(不管是不是阻塞IO,一個線程等待io操作時都會被操作系統(tǒng)掛起,不消耗CPU。),直到等到信號的傳遞。
就緒狀態(tài):當(dāng)進(jìn)程已分配到除CPU以外的所有必要的資源,只要獲得處理機可立即執(zhí)行,這時的進(jìn)程狀態(tài)稱為就緒狀態(tài)。執(zhí)行、運行:當(dāng)程序已獲得處理機,其程序正在處理機上執(zhí)行,此時的進(jìn)程狀態(tài)稱為執(zhí)行狀態(tài)。
阻塞:由于等待某個事件發(fā)生而無法執(zhí)行時,便放棄處理機而處于阻塞狀態(tài)。引進(jìn)進(jìn)程阻塞的事件可有多種,例如,等待I/O完成(IO是輸入input輸出output的首字母縮寫形式)、申請緩沖區(qū)不能滿足、等待信號等。
同步、異步
同步:
就是一個任務(wù)的完成需要依賴另一個任務(wù)時,只有等待被依賴的任務(wù)完成后,依賴的任務(wù)才能完成,這是一種可靠的任務(wù)序列。要么成功都成功,失敗都失敗,兩個任務(wù)的狀態(tài)可以保持一致。
#同步 兩件事 一件做完再做另外一件
異步:
是不需要等待被依賴的任務(wù)完成,只是通知被依賴的任務(wù)要完成什么工作,依賴的任務(wù)也立即執(zhí)行,只要自己完成了整個任務(wù)就算完成了,至于被依賴的任務(wù)最終是否完成,依賴它的任務(wù)無法確定,所以它是不可靠的任務(wù)序列。
# 異步 兩件事 同時做
進(jìn)程的創(chuàng)建
但凡是硬件,都需要有操作系統(tǒng)去管理,只要有操作系統(tǒng),就有進(jìn)程的概念,就需要有創(chuàng)建進(jìn)程的方式。
對于通用操作系統(tǒng)(跑很多應(yīng)用程序),需要有系統(tǒng)運行過程中創(chuàng)建或撤銷進(jìn)程的能力,主要分成4種形式創(chuàng)建新的進(jìn)程:
系統(tǒng)初始化(運行在后臺并且只在需要時才喚醒的進(jìn)程)
一個進(jìn)程在運行過程中開啟了子進(jìn)程(如nginx開啟多進(jìn)程)
用戶的交互請求,而創(chuàng)建一個進(jìn)程(如雙擊大家都熟悉的快播)
一個批處理作業(yè)的初始化(只在大型機的批處理系統(tǒng)中應(yīng)用)
無論哪一種,
新進(jìn)程的創(chuàng)建都是由一個已近存在的進(jìn)程執(zhí)行了一個用于創(chuàng)建進(jìn)程的系統(tǒng)調(diào)用而創(chuàng)建的。
進(jìn)程的結(jié)束:正常退出、出錯退出、嚴(yán)重錯誤、被其他進(jìn)程殺死。
Python中進(jìn)程的操作
之前我們已經(jīng)了解了很多進(jìn)程相關(guān)的理論知識,了解進(jìn)程是什么應(yīng)該不再困難了,剛剛我們已經(jīng)了解了,運行中的程序就是進(jìn)程。所有的進(jìn)程都是通過它的父進(jìn)程來創(chuàng)建的。因此,運行起來的Python程序也是一個進(jìn)程,那么我們也可以在程序中再創(chuàng)建進(jìn)程。多個進(jìn)程可以實現(xiàn)并發(fā)效果,也就是說,當(dāng)我們的程序中存在多個進(jìn)程的時候,在某些時候,就會讓程序的執(zhí)行速度變快。以我們之前所學(xué)的知識,并不能實現(xiàn)創(chuàng)建進(jìn)程這個功能,所以我們就需要借助Python程序中強到大的模塊——multiprocessing。
仔細(xì)來說,multiprocessing不是一個模塊而是Python中一個操作、管理進(jìn)程的包。之所以叫multi是取自multiple的多功能的意思,在這個包中幾乎包含了和進(jìn)程有關(guān)的所有子模塊。由于提供的子模塊非常多,為了方便大家歸類記憶我們分成四個部分:創(chuàng)建進(jìn)程部分、進(jìn)程同步部分、進(jìn)程池部分、進(jìn)程之間數(shù)據(jù)共享。
一、multiprocessing.Process模塊
Process模塊是一個創(chuàng)建進(jìn)程的模塊,語法格式為:
Process(target=運行的函數(shù)的內(nèi)存地址,name=自定義進(jìn)程名稱可不寫,args=(參數(shù)))
思考:Pycharm、py文件所在的進(jìn)程、子進(jìn)程p是同步還是異步?
py文件的運行必須要建立在pycharm的基礎(chǔ)上嗎?
很明顯不用,pycharm只是一個編譯器,只是一個更方便我們編程的工具而已,所以肯定是異步的。
問題很多的小明就要問了:那你子進(jìn)程p要建立在py文件的基礎(chǔ)上啊。
那我們檢驗一下:
從運行結(jié)果來看,子進(jìn)程和父進(jìn)程是同時執(zhí)行的,所以它們其實也是異步的。
二、Why __name__ =='__main__' ?
為什么要寫if __name_=='__main__': ?
只要是在windows系統(tǒng)上,并且創(chuàng)建子進(jìn)程,那么就必須寫。
原因是不同操作系統(tǒng)之間創(chuàng)建子進(jìn)程的方式不一樣,具體表現(xiàn)為:
Python多進(jìn)程中,對于子進(jìn)程的運行機制是:每個子進(jìn)程中,由于不同的進(jìn)程之間有獨立內(nèi)存,不會共享,所以每個子進(jìn)程是通過分別導(dǎo)入所在的腳本模塊來實現(xiàn)目標(biāo)函數(shù)的運行的。對于這個機制,有以下兩點需要特別注意。
由于每個子進(jìn)程是通過導(dǎo)入所在腳本的模塊實現(xiàn)模塊中函數(shù)的調(diào)用的,所以,為了避免將創(chuàng)建子進(jìn)程的語句也被導(dǎo)入(因為這樣就會造成無限循環(huán)創(chuàng)建子進(jìn)程,這顯然是不允許的,因此python禁止了在子進(jìn)程中再創(chuàng)建子進(jìn)程,否則會報錯),創(chuàng)建子進(jìn)程的語句必須在if __name__=='__main__'語句之后定義,或者如果創(chuàng)建子進(jìn)程的語句是定義在一個函數(shù)中的,那么這個函數(shù)調(diào)用必須在if __name__=='__main__'語句之后,這是python多進(jìn)程中的強制性語法規(guī)則。
由于子進(jìn)程可直接調(diào)用的是被導(dǎo)入模塊中的屬性,因此,子進(jìn)程中的目標(biāo)函數(shù)應(yīng)該是被導(dǎo)入的,這樣子進(jìn)程才可以調(diào)用到期需要的目標(biāo)函數(shù),因此,目標(biāo)函數(shù)必須在if __name__=='__main__'語句之前定義,如果是在該語句之后定義,那么由于被導(dǎo)入時這部分是不會被導(dǎo)入的,所以運行時就會報"被導(dǎo)入的主模塊沒有目標(biāo)函數(shù)屬性"這樣的錯誤
三、能否給子進(jìn)程傳參?
試一試不就知道了:
四、進(jìn)程間的數(shù)據(jù)隔離(數(shù)據(jù)不共享)
按照我們的理解,num在經(jīng)過global的聲明之后,在全局中其表現(xiàn)形式應(yīng)該發(fā)生了改變,但是我們看結(jié)果:
子進(jìn)程與父進(jìn)程的變量的num仍然沒有發(fā)生改變,說明進(jìn)程間數(shù)據(jù)是不共享的,有各自的內(nèi)存池。
五、如何開啟多進(jìn)程
就...正常...開啟...
六、子進(jìn)程和父進(jìn)程的關(guān)系
父進(jìn)程和子進(jìn)程的啟動是異步的;
父進(jìn)程只負(fù)責(zé)通知操作系統(tǒng)啟動子進(jìn)程;
接下來的工作由操作系統(tǒng)接手,父進(jìn)程繼續(xù)執(zhí)行;
父進(jìn)程的代碼執(zhí)行完畢之后并不會直接結(jié)束程序,需要等全部子進(jìn)程執(zhí)行完畢;
父進(jìn)程要負(fù)責(zé)回收子進(jìn)程的資源。
審核編輯:劉清
-
編譯器
+關(guān)注
關(guān)注
1文章
1618瀏覽量
49052 -
python
+關(guān)注
關(guān)注
56文章
4782瀏覽量
84460 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
202瀏覽量
13947
發(fā)布評論請先 登錄
相關(guān)推薦
評論