進(jìn)程
操作系統(tǒng)背景知識
顧名思義萨赁,進(jìn)程即正在執(zhí)行的一個過程弊琴。進(jìn)程是對正在運(yùn)行程序的一個抽象。
進(jìn)程的概念起源于操作系統(tǒng)杖爽,是操作系統(tǒng)最核心的概念敲董,也是操作系統(tǒng)提供的最古老也是最重要的抽象概念之一。操作系統(tǒng)的其他所有內(nèi)容都是圍繞進(jìn)程的概念展開的慰安。
所以想要真正了解進(jìn)程腋寨,必須事先了解操作系統(tǒng),點(diǎn)擊進(jìn)入? ??
PS:即使可以利用的cpu只有一個(早期的計(jì)算機(jī)確實(shí)如此)化焕,也能保證支持(偽)并發(fā)的能力萄窜。將一個單獨(dú)的cpu變成多個虛擬的cpu(多道技術(shù):時間多路復(fù)用和空間多路復(fù)用+硬件上支持隔離),沒有進(jìn)程的抽象撒桨,現(xiàn)代計(jì)算機(jī)將不復(fù)存在脂倦。
必備的理論基礎(chǔ):
#一 操作系統(tǒng)的作用:? ? 1:隱藏丑陋復(fù)雜的硬件接口,提供良好的抽象接口
? ? 2:管理元莫、調(diào)度進(jìn)程赖阻,并且將多個進(jìn)程對硬件的競爭變得有序#二 多道技術(shù):? ? 1.產(chǎn)生背景:針對單核,實(shí)現(xiàn)并發(fā)
? ? ps:
? ? 現(xiàn)在的主機(jī)一般是多核踱蠢,那么每個核都會利用多道技術(shù)
? ? 有4個cpu火欧,運(yùn)行于cpu1的某個程序遇到io阻塞棋电,會等到io結(jié)束再重新調(diào)度,會被調(diào)度到4個
? ? cpu中的任意一個苇侵,具體由操作系統(tǒng)調(diào)度算法決定赶盔。
? ? 2.空間上的復(fù)用:如內(nèi)存中同時有多道程序
? ? 3.時間上的復(fù)用:復(fù)用一個cpu的時間片
? ? ? 強(qiáng)調(diào):遇到io切,占用cpu時間過長也切榆浓,核心在于切之前將進(jìn)程的狀態(tài)保存下來于未,這樣
? ? ? ? ? ? 才能保證下次切換回來時,能基于上次切走的位置繼續(xù)運(yùn)行
什么是進(jìn)程
進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動陡鹃,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位烘浦,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進(jìn)程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中萍鲸,進(jìn)程是程序的基本執(zhí)行實(shí)體闷叉;在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器脊阴。程序是指令握侧、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實(shí)體嘿期。
狹義定義:進(jìn)程是正在運(yùn)行的程序的實(shí)例(an instance of a computer program that is being executed)品擎。
廣義定義:進(jìn)程是一個具有一定獨(dú)立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運(yùn)行活動。它是操作系統(tǒng)動態(tài)執(zhí)行的基本單元备徐,在傳統(tǒng)的操作系統(tǒng)中萄传,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元坦喘。
第一,進(jìn)程是一個實(shí)體西设。每一個進(jìn)程都有它自己的地址空間瓣铣,一般情況下,包括文本區(qū)域(text region)贷揽、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)棠笑。文本區(qū)域存儲處理器執(zhí)行的代碼;數(shù)據(jù)區(qū)域存儲變量和進(jìn)程執(zhí)行期間使用的動態(tài)分配的內(nèi)存禽绪;堆棧區(qū)域存儲著活動過程調(diào)用的指令和本地變量蓖救。
第二,進(jìn)程是一個“執(zhí)行中的程序”印屁。程序是一個沒有生命的實(shí)體循捺,只有處理器賦予程序生命時(操作系統(tǒng)執(zhí)行之),它才能成為一個活動的實(shí)體雄人,我們稱其為進(jìn)程从橘。[3]
進(jìn)程是操作系統(tǒng)中最基本念赶、重要的概念。是多道程序系統(tǒng)出現(xiàn)后恰力,為了刻畫系統(tǒng)內(nèi)部出現(xiàn)的動態(tài)情況叉谜,描述系統(tǒng)內(nèi)部各道程序的活動規(guī)律引進(jìn)的一個概念,所有多道程序設(shè)計(jì)操作系統(tǒng)都建立在進(jìn)程的基礎(chǔ)上。
從理論角度看踩萎,是對正在運(yùn)行的程序過程的抽象停局;
從實(shí)現(xiàn)角度看,是一種數(shù)據(jù)結(jié)構(gòu)香府,目的在于清晰地刻畫動態(tài)系統(tǒng)的內(nèi)在規(guī)律董栽,有效管理和調(diào)度進(jìn)入計(jì)算機(jī)系統(tǒng)主存儲器運(yùn)行的程序。
動態(tài)性:進(jìn)程的實(shí)質(zhì)是程序在多道程序系統(tǒng)中的一次執(zhí)行過程回还,進(jìn)程是動態(tài)產(chǎn)生裆泳,動態(tài)消亡的。
并發(fā)性:任何進(jìn)程都可以同其他進(jìn)程一起并發(fā)執(zhí)行
獨(dú)立性:進(jìn)程是一個能獨(dú)立運(yùn)行的基本單位柠硕,同時也是系統(tǒng)分配資源和調(diào)度的獨(dú)立單位工禾;
異步性:由于進(jìn)程間的相互制約,使進(jìn)程具有執(zhí)行的間斷性蝗柔,即進(jìn)程按各自獨(dú)立的闻葵、不可預(yù)知的速度向前推進(jìn)
結(jié)構(gòu)特征:進(jìn)程由程序、數(shù)據(jù)和進(jìn)程控制塊三部分組成癣丧。
多個不同的進(jìn)程可以包含相同的程序:一個程序在不同的數(shù)據(jù)集里就構(gòu)成不同的進(jìn)程槽畔,能得到不同的結(jié)果;但是執(zhí)行過程中胁编,程序不能發(fā)生改變厢钧。
程序是指令和數(shù)據(jù)的有序集合,其本身沒有任何運(yùn)行的含義嬉橙,是一個靜態(tài)的概念早直。
而進(jìn)程是程序在處理機(jī)上的一次執(zhí)行過程,它是一個動態(tài)的概念市框。
程序可以作為一種軟件資料長期存在霞扬,而進(jìn)程是有一定生命期的。
程序是永久的枫振,進(jìn)程是暫時的喻圃。
注意:同一個程序執(zhí)行兩次,就會在操作系統(tǒng)中出現(xiàn)兩個進(jìn)程粪滤,所以我們可以同時運(yùn)行一個軟件斧拍,分別做不同的事情也不會混亂。
進(jìn)程調(diào)度
要想多個進(jìn)程交替運(yùn)行杖小,操作系統(tǒng)必須對這些進(jìn)程進(jìn)行調(diào)度饮焦,這個調(diào)度也不是隨即進(jìn)行的怕吴,而是需要遵循一定的法則,由此就有了進(jìn)程的調(diào)度算法县踢。
先來先服務(wù)(FCFS)調(diào)度算法是一種最簡單的調(diào)度算法转绷,該算法既可用于作業(yè)調(diào)度,也可用于進(jìn)程調(diào)度硼啤。FCFS算法比較有利于長作業(yè)(進(jìn)程)议经,而不利于短作業(yè)(進(jìn)程)。由此可知谴返,本算法適合于CPU繁忙型作業(yè)煞肾,而不利于I/O繁忙型的作業(yè)(進(jìn)程)。
短作業(yè)(進(jìn)程)優(yōu)先調(diào)度算法(SJ/PF)是指對短作業(yè)或短進(jìn)程優(yōu)先調(diào)度的算法嗓袱,該算法既可用于作業(yè)調(diào)度籍救,也可用于進(jìn)程調(diào)度。但其對長作業(yè)不利渠抹;不能保證緊迫性作業(yè)(進(jìn)程)被及時處理蝙昙;作業(yè)的長短只是被估算出來的。
? ? ? 時間片輪轉(zhuǎn)(Round Robin梧却,RR)法的基本思路是讓每個進(jìn)程在就緒隊(duì)列中的等待時間與享受服務(wù)的時間成比例奇颠。在時間片輪轉(zhuǎn)法中,需要將CPU的處理時間分成固定大小的時間片放航,例如烈拒,幾十毫秒至幾百毫秒。如果一個進(jìn)程在被調(diào)度選中之后用完了系統(tǒng)規(guī)定的時間片广鳍,但又未完成要求的任務(wù)荆几,則它自行釋放自己所占有的CPU而排到就緒隊(duì)列的末尾,等待下一次調(diào)度赊时。同時吨铸,進(jìn)程調(diào)度程序又去調(diào)度當(dāng)前就緒隊(duì)列中的第一個進(jìn)程。
? ? ? 顯然蛋叼,輪轉(zhuǎn)法只能用來調(diào)度分配一些可以搶占的資源焊傅。這些可以搶占的資源可以隨時被剝奪剂陡,而且可以將它們再分配給別的進(jìn)程狈涮。CPU是可搶占資源的一種。但打印機(jī)等資源是不可搶占的鸭栖。由于作業(yè)調(diào)度是對除了CPU之外的所有系統(tǒng)硬件資源的分配歌馍,其中包含有不可搶占資源,所以作業(yè)調(diào)度不使用輪轉(zhuǎn)法晕鹊。
在輪轉(zhuǎn)法中松却,時間片長度的選取非常重要暴浦。首先,時間片長度的選擇會直接影響到系統(tǒng)的開銷和響應(yīng)時間晓锻。如果時間片長度過短歌焦,則調(diào)度程序搶占處理機(jī)的次數(shù)增多。這將使進(jìn)程上下文切換次數(shù)也大大增加砚哆,從而加重系統(tǒng)開銷独撇。反過來,如果時間片長度選擇過長躁锁,例如纷铣,一個時間片能保證就緒隊(duì)列中所需執(zhí)行時間最長的進(jìn)程能執(zhí)行完畢,則輪轉(zhuǎn)法變成了先來先服務(wù)法战转。時間片長度的選擇是根據(jù)系統(tǒng)對響應(yīng)時間的要求和就緒隊(duì)列中所允許最大的進(jìn)程數(shù)來確定的搜立。
? ? ? 在輪轉(zhuǎn)法中,加入到就緒隊(duì)列的進(jìn)程有3種情況:
? ? ? 一種是分給它的時間片用完槐秧,但進(jìn)程還未完成啄踊,回到就緒隊(duì)列的末尾等待下次調(diào)度去繼續(xù)執(zhí)行。
? ? ? 另一種情況是分給該進(jìn)程的時間片并未用完色鸳,只是因?yàn)檎埱驣/O或由于進(jìn)程的互斥與同步關(guān)系而被阻塞社痛。當(dāng)阻塞解除之后再回到就緒隊(duì)列。
? ? ? 第三種情況就是新創(chuàng)建進(jìn)程進(jìn)入就緒隊(duì)列命雀。
? ? ? 如果對這些進(jìn)程區(qū)別對待蒜哀,給予不同的優(yōu)先級和時間片從直觀上看,可以進(jìn)一步改善系統(tǒng)服務(wù)質(zhì)量和效率吏砂。例如撵儿,我們可把就緒隊(duì)列按照進(jìn)程到達(dá)就緒隊(duì)列的類型和進(jìn)程被阻塞時的阻塞原因分成不同的就緒隊(duì)列,每個隊(duì)列按FCFS原則排列狐血,各隊(duì)列之間的進(jìn)程享有不同的優(yōu)先級淀歇,但同一隊(duì)列內(nèi)優(yōu)先級相同。這樣匈织,當(dāng)一個進(jìn)程在執(zhí)行完它的時間片之后浪默,或從睡眠中被喚醒以及被創(chuàng)建之后,將進(jìn)入不同的就緒隊(duì)列缀匕。?
前面介紹的各種用作進(jìn)程調(diào)度的算法都有一定的局限性纳决。如短進(jìn)程優(yōu)先的調(diào)度算法,僅照顧了短進(jìn)程而忽略了長進(jìn)程乡小,而且如果并未指明進(jìn)程的長度阔加,則短進(jìn)程優(yōu)先和基于進(jìn)程長度的搶占式調(diào)度算法都將無法使用。
而多級反饋隊(duì)列調(diào)度算法則不必事先知道各種進(jìn)程所需的執(zhí)行時間满钟,而且還可以滿足各種類型進(jìn)程的需要胜榔,因而它是目前被公認(rèn)的一種較好的進(jìn)程調(diào)度算法胳喷。在采用多級反饋隊(duì)列調(diào)度算法的系統(tǒng)中,調(diào)度算法的實(shí)施過程如下所述夭织。
(1) 應(yīng)設(shè)置多個就緒隊(duì)列吭露,并為各個隊(duì)列賦予不同的優(yōu)先級。第一個隊(duì)列的優(yōu)先級最高尊惰,第二個隊(duì)列次之奴饮,其余各隊(duì)列的優(yōu)先權(quán)逐個降低。該算法賦予各個隊(duì)列中進(jìn)程執(zhí)行時間片的大小也各不相同择浊,在優(yōu)先權(quán)愈高的隊(duì)列中戴卜,為每個進(jìn)程所規(guī)定的執(zhí)行時間片就愈小。例如琢岩,第二個隊(duì)列的時間片要比第一個隊(duì)列的時間片長一倍投剥,……,第i+1個隊(duì)列的時間片要比第i個隊(duì)列的時間片長一倍担孔。
(2) 當(dāng)一個新進(jìn)程進(jìn)入內(nèi)存后江锨,首先將它放入第一隊(duì)列的末尾,按FCFS原則排隊(duì)等待調(diào)度糕篇。當(dāng)輪到該進(jìn)程執(zhí)行時啄育,如它能在該時間片內(nèi)完成,便可準(zhǔn)備撤離系統(tǒng)拌消;如果它在一個時間片結(jié)束時尚未完成挑豌,調(diào)度程序便將該進(jìn)程轉(zhuǎn)入第二隊(duì)列的末尾,再同樣地按FCFS原則等待調(diào)度執(zhí)行墩崩;如果它在第二隊(duì)列中運(yùn)行一個時間片后仍未完成氓英,再依次將它放入第三隊(duì)列,……鹦筹,如此下去铝阐,當(dāng)一個長作業(yè)(進(jìn)程)從第一隊(duì)列依次降到第n隊(duì)列后,在第n 隊(duì)列便采取按時間片輪轉(zhuǎn)的方式運(yùn)行铐拐。
(3) 僅當(dāng)?shù)谝魂?duì)列空閑時徘键,調(diào)度程序才調(diào)度第二隊(duì)列中的進(jìn)程運(yùn)行;僅當(dāng)?shù)?~(i-1)隊(duì)列均空時遍蟋,才會調(diào)度第i隊(duì)列中的進(jìn)程運(yùn)行吹害。如果處理機(jī)正在第i隊(duì)列中為某進(jìn)程服務(wù)時,又有新進(jìn)程進(jìn)入優(yōu)先權(quán)較高的隊(duì)列(第1~(i-1)中的任何一個隊(duì)列)匿值,則此時新進(jìn)程將搶占正在運(yùn)行進(jìn)程的處理機(jī)赠制,即由調(diào)度程序把正在運(yùn)行的進(jìn)程放回到第i隊(duì)列的末尾赂摆,把處理機(jī)分配給新到的高優(yōu)先權(quán)進(jìn)程挟憔。
進(jìn)程的并行與并發(fā)
并行?:?并行是指兩者同時執(zhí)行钟些,比如賽跑,兩個人都在不停的往前跑绊谭;(資源夠用政恍,比如三個線程,四核的CPU )
并發(fā)?:?并發(fā)是指資源有限的情況下达传,兩者交替輪流使用資源篙耗,比如一段路(單核CPU資源)同時只能過一個人,A走一段后宪赶,讓給B宗弯,B用完繼續(xù)給A ,交替使用搂妻,目的是提高效率蒙保。
區(qū)別:
并行是從微觀上,也就是在一個精確的時間片刻欲主,有不同的程序在執(zhí)行邓厕,這就要求必須有多個處理器泉蝌。
并發(fā)是從宏觀上悯辙,在一個時間段上可以看出是同時執(zhí)行的了赵,比如一個服務(wù)器同時處理多個session嫌变。
同步異步阻塞非阻塞
狀態(tài)介紹
在了解其他概念之前刹前,我們首先要了解進(jìn)程的幾個狀態(tài)丹允。在程序運(yùn)行的過程中筋蓖,由于被操作系統(tǒng)的調(diào)度算法控制薪介,程序會進(jìn)入幾個狀態(tài):就緒伟桅,運(yùn)行和阻塞硅堆。
(1)就緒(Ready)狀態(tài)
當(dāng)進(jìn)程已分配到除CPU以外的所有必要的資源贿讹,只要獲得處理機(jī)便可立即執(zhí)行渐逃,這時的進(jìn)程狀態(tài)稱為就緒狀態(tài)。
∶窆印(2)執(zhí)行/運(yùn)行(Running)狀態(tài)當(dāng)進(jìn)程已獲得處理機(jī)茄菊,其程序正在處理機(jī)上執(zhí)行,此時的進(jìn)程狀態(tài)稱為執(zhí)行狀態(tài)赊堪。
∶嬷场(3)阻塞(Blocked)狀態(tài)正在執(zhí)行的進(jìn)程,由于等待某個事件發(fā)生而無法執(zhí)行時哭廉,便放棄處理機(jī)而處于阻塞狀態(tài)脊僚。引起進(jìn)程阻塞的事件可有多種,例如,等待I/O完成辽幌、申請緩沖區(qū)不能滿足增淹、等待信件(信號)等。
同步和異步
? ? ??所謂同步就是一個任務(wù)的完成需要依賴另外一個任務(wù)時乌企,只有等待被依賴的任務(wù)完成后虑润,依賴的任務(wù)才能算完成,這是一種可靠的任務(wù)序列加酵。要么成功都成功拳喻,失敗都失敗,兩個任務(wù)的狀態(tài)可以保持一致猪腕。
所謂異步是不需要等待被依賴的任務(wù)完成冗澈,只是通知被依賴的任務(wù)要完成什么工作,依賴的任務(wù)也立即執(zhí)行陋葡,只要自己完成了整個任務(wù)就算完成了渗柿。至于被依賴的任務(wù)最終是否真正完成,依賴它的任務(wù)無法確定脖岛,所以它是不可靠的任務(wù)序列朵栖。
比如我去銀行辦理業(yè)務(wù),可能會有兩種方式:
第一種 :選擇排隊(duì)等候柴梆;
第二種 :選擇取一個小紙條上面有我的號碼陨溅,等到排到我這一號時由柜臺的人通知我輪到我去辦理業(yè)務(wù)了;
第一種:前者(排隊(duì)等候)就是同步等待消息通知绍在,也就是我要一直在等待銀行辦理業(yè)務(wù)情況门扇;
第二種:后者(等待別人通知)就是異步等待消息通知。在異步消息處理中偿渡,等待消息通知者(在這個例子中就是等待辦理業(yè)務(wù)的人)往往注冊一個回調(diào)機(jī)制臼寄,在所等待的事件被觸發(fā)時由觸發(fā)機(jī)制(在這里是柜臺的人)通過某種機(jī)制(在這里是寫在小紙條上的號碼,喊號)找到等待該事件的人溜宽。
阻塞與非阻塞
? ? ??阻塞和非阻塞這兩個概念與程序(線程)等待消息通知(無所謂同步或者異步)時的狀態(tài)有關(guān)吉拳。也就是說阻塞與非阻塞主要是程序(線程)等待消息通知時的狀態(tài)角度來說的
繼續(xù)上面的那個例子,不論是排隊(duì)還是使用號碼等待通知适揉,如果在這個等待的過程中留攒,等待者除了等待消息通知之外不能做其它的事情,那么該機(jī)制就是阻塞的嫉嘀,表現(xiàn)在程序中,也就是該程序一直阻塞在該函數(shù)調(diào)用處不能繼續(xù)往下執(zhí)行炼邀。
相反,有的人喜歡在銀行辦理這些業(yè)務(wù)的時候一邊打打電話發(fā)發(fā)短信一邊等待剪侮,這樣的狀態(tài)就是非阻塞的拭宁,因?yàn)樗?等待者)沒有阻塞在這個消息通知上,而是一邊做自己的事情一邊等待。
注意:同步非阻塞形式實(shí)際上是效率低下的杰标,想象一下你一邊打著電話一邊還需要抬頭看到底隊(duì)伍排到你了沒有兵怯。如果把打電話和觀察排隊(duì)的位置看成是程序的兩個操作的話,這個程序需要在這兩種不同的行為之間來回的切換在旱,效率可想而知是低下的;而異步非阻塞形式卻沒有這樣的問題推掸,因?yàn)榇螂娫捠悄?等待者)的事情桶蝎,而通知你則是柜臺(消息觸發(fā)機(jī)制)的事情,程序沒有在兩種不同的操作中來回切換谅畅。
同步/異步與阻塞/非阻塞
同步阻塞形式
效率最低登渣。拿上面的例子來說,就是你專心排隊(duì)毡泻,什么別的事都不做胜茧。
異步阻塞形式
如果在銀行等待辦理業(yè)務(wù)的人采用的是異步的方式去等待消息被觸發(fā)(通知),也就是領(lǐng)了一張小紙條仇味,假如在這段時間里他不能離開銀行做其它的事情呻顽,那么很顯然,這個人被阻塞在了這個等待的操作上面丹墨;
異步操作是可以被阻塞住的廊遍,只不過它不是在處理消息時阻塞,而是在等待消息通知時被阻塞贩挣。
同步非阻塞形式
實(shí)際上是效率低下的喉前。
想象一下你一邊打著電話一邊還需要抬頭看到底隊(duì)伍排到你了沒有,如果把打電話和觀察排隊(duì)的位置看成是程序的兩個操作的話王财,這個程序需要在這兩種不同的行為之間來回的切換卵迂,效率可想而知是低下的。
異步非阻塞形式
效率更高绒净,
因?yàn)榇螂娫捠悄?等待者)的事情见咒,而通知你則是柜臺(消息觸發(fā)機(jī)制)的事情,程序沒有在兩種不同的操作中來回切換挂疆。
比如說论颅,這個人突然發(fā)覺自己煙癮犯了,需要出去抽根煙囱嫩,于是他告訴大堂經(jīng)理說恃疯,排到我這個號碼的時候麻煩到外面通知我一下,那么他就沒有被阻塞在這個等待的操作上面墨闲,自然這個就是異步+非阻塞的方式了今妄。
很多人會把同步和阻塞混淆,是因?yàn)楹芏鄷r候同步操作會以阻塞的形式表現(xiàn)出來,同樣的盾鳞,很多人也會把異步和非阻塞混淆犬性,因?yàn)楫惒讲僮饕话愣疾粫谡嬲腎O操作處被阻塞。
進(jìn)程的創(chuàng)建與結(jié)束
進(jìn)程的創(chuàng)建
但凡是硬件腾仅,都需要有操作系統(tǒng)去管理乒裆,只要有操作系統(tǒng),就有進(jìn)程的概念推励,就需要有創(chuàng)建進(jìn)程的方式鹤耍,一些操作系統(tǒng)只為一個應(yīng)用程序設(shè)計(jì),比如微波爐中的控制器验辞,一旦啟動微波爐稿黄,所有的進(jìn)程都已經(jīng)存在。
而對于通用系統(tǒng)(跑很多應(yīng)用程序)跌造,需要有系統(tǒng)運(yùn)行過程中創(chuàng)建或撤銷進(jìn)程的能力杆怕,主要分為4中形式創(chuàng)建新的進(jìn)程:
1. 系統(tǒng)初始化(查看進(jìn)程linux中用ps命令,windows中用任務(wù)管理器壳贪,前臺進(jìn)程負(fù)責(zé)與用戶交互陵珍,后臺運(yùn)行的進(jìn)程與用戶無關(guān),運(yùn)行在后臺并且只在需要時才喚醒的進(jìn)程违施,稱為守護(hù)進(jìn)程撑教,如電子郵件、web頁面醉拓、新聞伟姐、打印)
2. 一個進(jìn)程在運(yùn)行過程中開啟了子進(jìn)程(如nginx開啟多進(jìn)程亿卤,os.fork,subprocess.Popen等)
3. 用戶的交互式請求愤兵,而創(chuàng)建一個新進(jìn)程(如用戶雙擊暴風(fēng)影音)
4. 一個批處理作業(yè)的初始化(只在大型機(jī)的批處理系統(tǒng)中應(yīng)用)
無論哪一種,新進(jìn)程的創(chuàng)建都是由一個已經(jīng)存在的進(jìn)程執(zhí)行了一個用于創(chuàng)建進(jìn)程的系統(tǒng)調(diào)用而創(chuàng)建的排吴。
1. 在UNIX中該系統(tǒng)調(diào)用是:fork秆乳,fork會創(chuàng)建一個與父進(jìn)程一模一樣的副本,二者有相同的存儲映像钻哩、同樣的環(huán)境字符串和同樣的打開文件(在shell解釋器進(jìn)程中屹堰,執(zhí)行一個命令就會創(chuàng)建一個子進(jìn)程)
2. 在windows中該系統(tǒng)調(diào)用是:CreateProcess,CreateProcess既處理進(jìn)程的創(chuàng)建街氢,也負(fù)責(zé)把正確的程序裝入新進(jìn)程扯键。
關(guān)于創(chuàng)建子進(jìn)程,UNIX和windows
1.相同的是:進(jìn)程創(chuàng)建后珊肃,父進(jìn)程和子進(jìn)程有各自不同的地址空間(多道技術(shù)要求物理層面實(shí)現(xiàn)進(jìn)程之間內(nèi)存的隔離)荣刑,任何一個進(jìn)程的在其地址空間中的修改都不會影響到另外一個進(jìn)程馅笙。
2.不同的是:在UNIX中,子進(jìn)程的初始地址空間是父進(jìn)程的一個副本厉亏,提示:子進(jìn)程和父進(jìn)程是可以有只讀的共享內(nèi)存區(qū)的董习。但是對于windows系統(tǒng)來說,從一開始父進(jìn)程與子進(jìn)程的地址空間就是不同的爱只。
進(jìn)程的結(jié)束
1. 正常退出(自愿皿淋,如用戶點(diǎn)擊交互式頁面的叉號,或程序執(zhí)行完畢調(diào)用發(fā)起系統(tǒng)調(diào)用正常退出恬试,在linux中用exit窝趣,在windows中用ExitProcess)
2. 出錯退出(自愿,python a.py中a.py不存在)
3. 嚴(yán)重錯誤(非自愿忘渔,執(zhí)行非法指令高帖,如引用不存在的內(nèi)存缰儿,1/0等畦粮,可以捕捉異常,try...except...)
4. 被其他進(jìn)程殺死(非自愿乖阵,如kill -9)
在python程序中的進(jìn)程操作
之前我們已經(jīng)了解了很多進(jìn)程相關(guān)的理論知識宣赔,了解進(jìn)程是什么應(yīng)該不再困難了,剛剛我們已經(jīng)了解了瞪浸,運(yùn)行中的程序就是一個進(jìn)程儒将。所有的進(jìn)程都是通過它的父進(jìn)程來創(chuàng)建的。因此对蒲,運(yùn)行起來的python程序也是一個進(jìn)程钩蚊,那么我們也可以在程序中再創(chuàng)建進(jìn)程。多個進(jìn)程可以實(shí)現(xiàn)并發(fā)效果蹈矮,也就是說砰逻,當(dāng)我們的程序中存在多個進(jìn)程的時候,在某些時候泛鸟,就會讓程序的執(zhí)行速度變快蝠咆。以我們之前所學(xué)的知識,并不能實(shí)現(xiàn)創(chuàng)建進(jìn)程這個功能北滥,所以我們就需要借助python中強(qiáng)大的模塊刚操。
multiprocess模塊
? ? ? 仔細(xì)說來,multiprocess不是一個模塊而是python中一個操作再芋、管理進(jìn)程的包菊霜。 之所以叫multi是取自multiple的多功能的意思,在這個包中幾乎包含了和進(jìn)程有關(guān)的所有子模塊。由于提供的子模塊非常多济赎,為了方便大家歸類記憶占卧,我將這部分大致分為四個部分:創(chuàng)建進(jìn)程部分遗菠,進(jìn)程同步部分,進(jìn)程池部分华蜒,進(jìn)程之間數(shù)據(jù)共享辙纬。
multiprocess.process模塊
process模塊介紹
process模塊是一個創(chuàng)建進(jìn)程的模塊,借助這個模塊叭喜,就可以完成進(jìn)程的創(chuàng)建贺拣。
Process([group [, target [, name [, args [, kwargs]]]]]),由該類實(shí)例化得到的對象捂蕴,表示一個子進(jìn)程中的任務(wù)(尚未啟動)
強(qiáng)調(diào):
1. 需要使用關(guān)鍵字的方式來指定參數(shù)
2. args指定的為傳給target函數(shù)的位置參數(shù)譬涡,是一個元組形式,必須有逗號
參數(shù)介紹:
1 group參數(shù)未使用啥辨,值始終為None
2 target表示調(diào)用對象涡匀,即子進(jìn)程要執(zhí)行的任務(wù)
3 args表示調(diào)用對象的位置參數(shù)元組,args=(1,2,'egon',)
4 kwargs表示調(diào)用對象的字典,kwargs={'name':'egon','age':18}
5 name為子進(jìn)程的名稱
方法介紹
1 p.start():啟動進(jìn)程溉知,并調(diào)用該子進(jìn)程中的p.run() 2 p.run():進(jìn)程啟動時運(yùn)行的方法陨瘩,正是它去調(diào)用target指定的函數(shù),我們自定義類的類中一定要實(shí)現(xiàn)該方法? 3 p.terminate():強(qiáng)制終止進(jìn)程p级乍,不會進(jìn)行任何清理操作舌劳,如果p創(chuàng)建了子進(jìn)程,該子進(jìn)程就成了僵尸進(jìn)程玫荣,使用該方法需要特別小心這種情況甚淡。如果p還保存了一個鎖那么也將不會被釋放,進(jìn)而導(dǎo)致死鎖4 p.is_alive():如果p仍然運(yùn)行捅厂,返回True5 p.join([timeout]):主線程等待p終止(強(qiáng)調(diào):是主線程處于等的狀態(tài)贯卦,而p是處于運(yùn)行的狀態(tài))。timeout是可選的超時時間焙贷,需要強(qiáng)調(diào)的是撵割,p.join只能join住start開啟的進(jìn)程,而不能join住run開啟的進(jìn)程?
屬性介紹
1 p.daemon:默認(rèn)值為False盈厘,如果設(shè)為True睁枕,代表p為后臺運(yùn)行的守護(hù)進(jìn)程,當(dāng)p的父進(jìn)程終止時沸手,p也隨之終止外遇,并且設(shè)定為True后,p不能創(chuàng)建自己的新進(jìn)程契吉,必須在p.start()之前設(shè)置2 p.name:進(jìn)程的名稱3 p.pid:進(jìn)程的pid4 p.exitcode:進(jìn)程在運(yùn)行時為None跳仿、如果為–N,表示被信號N結(jié)束(了解即可)5 p.authkey:進(jìn)程的身份驗(yàn)證鍵,默認(rèn)是由os.urandom()隨機(jī)生成的32字符的字符串捐晶。這個鍵的用途是為涉及網(wǎng)絡(luò)連接的底層進(jìn)程間通信提供安全性菲语,這類連接只有在具有相同的身份驗(yàn)證鍵時才能成功(了解即可)
在windows中使用process模塊的注意事項(xiàng)
在Windows操作系統(tǒng)中由于沒有fork(linux操作系統(tǒng)中創(chuàng)建進(jìn)程的機(jī)制)妄辩,在創(chuàng)建子進(jìn)程的時候會自動 import 啟動它的這個文件,而在 import 的時候又執(zhí)行了整個文件山上。因此如果將process()直接寫在文件中就會無限遞歸創(chuàng)建子進(jìn)程報錯眼耀。所以必須把創(chuàng)建子進(jìn)程的部分使用if __name__ ==‘__main__’ 判斷保護(hù)起來,import 的時候? 佩憾,就不會遞歸運(yùn)行了哮伟。
使用process模塊創(chuàng)建進(jìn)程
在一個python進(jìn)程中開啟子進(jìn)程,start方法和并發(fā)效果妄帘。
在python中啟動的第一個子進(jìn)程
import timefrom multiprocessing import Processdef f(name):
? ? print('hello', name)
? ? print('我是子進(jìn)程')if __name__ == '__main__':
? ? p = Process(target=f, args=('bob',))
? ? p.start()
? ? time.sleep(1)
? ? print('執(zhí)行主進(jìn)程的內(nèi)容了')
join方法
import timefrom multiprocessing import Processdef f(name):
? ? print('hello', name)
? ? time.sleep(1)
? ? print('我是子進(jìn)程')if __name__ == '__main__':
? ? p = Process(target=f, args=('bob',))
? ? p.start()
? ? #p.join() 主線程等待p終止
? ? print('我是父進(jìn)程')
查看主進(jìn)程和子進(jìn)程的進(jìn)程號
import osfrom multiprocessing import Processdef f(x):
? ? print('子進(jìn)程id :',os.getpid(),'父進(jìn)程id :',os.getppid())
? ? return x*xif __name__ == '__main__':
? ? print('主進(jìn)程id :', os.getpid())
? ? p_lst = []
? ? for i in range(5):
? ? ? ? p = Process(target=f, args=(i,))
? ? ? ? p.start()
進(jìn)階楞黄,多個進(jìn)程同時運(yùn)行(注意,子進(jìn)程的執(zhí)行順序不是根據(jù)啟動順序決定的)
多個進(jìn)程同時運(yùn)行?
import timefrom multiprocessing import Processdef f(name):
? ? print('hello', name)
? ? time.sleep(1)if __name__ == '__main__':
? ? p_lst = []
? ? for i in range(5):
? ? ? ? p = Process(target=f, args=('bob',))
? ? ? ? p.start()
? ? ? ? p_lst.append(p)
多個進(jìn)程同時運(yùn)行抡驼,再談join方法(1)
import timefrom multiprocessing import Processdef f(name):
? ? print('hello', name)
? ? time.sleep(1)if __name__ == '__main__':
? ? p_lst = []
? ? for i in range(5):
? ? ? ? p = Process(target=f, args=('bob',))
? ? ? ? p.start()
? ? ? ? p_lst.append(p)
? ? ? ? p.join()
? ? # [p.join() for p in p_lst]? ? print('父進(jìn)程在執(zhí)行')
多個進(jìn)程同時運(yùn)行鬼廓,再談join方法(2)
import timefrom multiprocessing import Processdef f(name):
? ? print('hello', name)
? ? time.sleep(1)if __name__ == '__main__':
? ? p_lst = []
? ? for i in range(5):
? ? ? ? p = Process(target=f, args=('bob',))
? ? ? ? p.start()
? ? ? ? p_lst.append(p)
? ? # [p.join() for p in p_lst]? ? print('父進(jìn)程在執(zhí)行')
除了上面這些開啟進(jìn)程的方法,還有一種以繼承Process類的形式開啟進(jìn)程的方式
通過繼承Process類開啟進(jìn)程
import osfrom multiprocessing import Processclass MyProcess(Process):
? ? def __init__(self,name):
? ? ? ? super().__init__()
? ? ? ? self.name=name
? ? def run(self):
? ? ? ? print(os.getpid())
? ? ? ? print('%s 正在和女主播聊天' %self.name)
p1=MyProcess('wupeiqi')
p2=MyProcess('yuanhao')
p3=MyProcess('nezha')
p1.start() #start會自動調(diào)用runp2.start()# p2.run()p3.start()
p1.join()
p2.join()
p3.join()print('主線程')
進(jìn)程之間的數(shù)據(jù)隔離問題
from multiprocessing import Processdef work():
? ? global n
? ? n=0
? ? print('子進(jìn)程內(nèi): ',n)if __name__ == '__main__':
? ? n = 100
? ? p=Process(target=work)
? ? p.start()
? ? print('主進(jìn)程內(nèi): ',n)
守護(hù)進(jìn)程
會隨著主進(jìn)程的結(jié)束而結(jié)束致盟。
主進(jìn)程創(chuàng)建守護(hù)進(jìn)程
其一:守護(hù)進(jìn)程會在主進(jìn)程代碼執(zhí)行結(jié)束后就終止
其二:守護(hù)進(jìn)程內(nèi)無法再開啟子進(jìn)程,否則拋出異常:AssertionError: daemonic processes are not allowed to have children
注意:進(jìn)程之間是互相獨(dú)立的碎税,主進(jìn)程代碼運(yùn)行結(jié)束,守護(hù)進(jìn)程隨即終止
守護(hù)進(jìn)程的啟動
import osimport timefrom multiprocessing import Processclass Myprocess(Process):
? ? def __init__(self,person):
? ? ? ? super().__init__()
? ? ? ? self.person = person
? ? def run(self):
? ? ? ? print(os.getpid(),self.name)
? ? ? ? print('%s正在和女主播聊天' %self.person)
p=Myprocess('哪吒')
p.daemon=True #一定要在p.start()前設(shè)置,設(shè)置p為守護(hù)進(jìn)程,禁止p創(chuàng)建子進(jìn)程,并且父進(jìn)程代碼執(zhí)行結(jié)束,p即終止運(yùn)行p.start()
time.sleep(10) # 在sleep時查看進(jìn)程id對應(yīng)的進(jìn)程ps -ef|grep idprint('主')
主進(jìn)程代碼執(zhí)行結(jié)束守護(hù)進(jìn)程立即結(jié)束
from multiprocessing import Processdef foo():
? ? print(123)
? ? time.sleep(1)
? ? print("end123")def bar():
? ? print(456)
? ? time.sleep(3)
? ? print("end456")
p1=Process(target=foo)
p2=Process(target=bar)
p1.daemon=True
p1.start()
p2.start()
time.sleep(0.1)print("main-------")#打印該行則主進(jìn)程代碼結(jié)束,則守護(hù)進(jìn)程p1應(yīng)該被終止.#可能會有p1任務(wù)執(zhí)行的打印信息123,因?yàn)橹鬟M(jìn)程打印main----時,p1也執(zhí)行了,但是隨即被終止.
socket聊天并發(fā)實(shí)例
使用多進(jìn)程實(shí)現(xiàn)socket聊天并發(fā)-server
from socket import *from multiprocessing import Process
server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)def talk(conn,client_addr):
? ? while True:
? ? ? ? try:
? ? ? ? ? ? msg=conn.recv(1024)
? ? ? ? ? ? if not msg:break? ? ? ? ? ? conn.send(msg.upper())
? ? ? ? except Exception:
? ? ? ? ? ? breakif __name__ == '__main__': #windows下start進(jìn)程一定要寫到這下面? ? while True:
? ? ? ? conn,client_addr=server.accept()
? ? ? ? p=Process(target=talk,args=(conn,client_addr))
? ? ? ? p.start()
client端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))while True:
? ? msg=input('>>: ').strip()
? ? if not msg:continue? ? client.send(msg.encode('utf-8'))
? ? msg=client.recv(1024)
? ? print(msg.decode('utf-8'))
多進(jìn)程中的其他方法
進(jìn)程對象的其他方法:terminate,is_alive
from multiprocessing import Processimport timeimport randomclass Myprocess(Process):
? ? def __init__(self,person):
? ? ? ? self.name=person
? ? ? ? super().__init__()
? ? def run(self):
? ? ? ? print('%s正在和網(wǎng)紅臉聊天' %self.name)
? ? ? ? time.sleep(random.randrange(1,5))
? ? ? ? print('%s還在和網(wǎng)紅臉聊天' %self.name)
p1=Myprocess('哪吒')
p1.start()
p1.terminate()#關(guān)閉進(jìn)程,不會立即關(guān)閉,所以is_alive立刻查看的結(jié)果可能還是存活print(p1.is_alive()) #結(jié)果為Trueprint('開始')print(p1.is_alive()) #結(jié)果為False
進(jìn)程對象的其他屬性:pid和name
1 class Myprocess(Process): 2? ? def __init__(self,person): 3? ? ? ? self.name=person? # name屬性是Process中的屬性勾邦,標(biāo)示進(jìn)程的名字 4? ? ? ? super().__init__() # 執(zhí)行父類的初始化方法會覆蓋name屬性 5? ? ? ? #self.name = person # 在這里設(shè)置就可以修改進(jìn)程名字了 6? ? ? ? #self.person = person #如果不想覆蓋進(jìn)程名蚣录,就修改屬性名稱就可以了 7? ? def run(self): 8? ? ? ? print('%s正在和網(wǎng)紅臉聊天' %self.name) 9? ? ? ? # print('%s正在和網(wǎng)紅臉聊天' %self.person)10? ? ? ? time.sleep(random.randrange(1,5))11? ? ? ? print('%s正在和網(wǎng)紅臉聊天' %self.name)12? ? ? ? # print('%s正在和網(wǎng)紅臉聊天' %self.person)13 14 15 p1=Myprocess('哪吒')16 p1.start()17 print(p1.pid)? ? #可以查看子進(jìn)程的進(jìn)程id
進(jìn)程同步(multiprocess.Lock割择、multiprocess.Semaphore眷篇、multiprocess.Event)
鎖 —— multiprocess.Lock
? ? ? 通過剛剛的學(xué)習(xí),我們千方百計(jì)實(shí)現(xiàn)了程序的異步荔泳,讓多個任務(wù)可以同時在幾個進(jìn)程中并發(fā)處理蕉饼,他們之間的運(yùn)行沒有順序,一旦開啟也不受我們控制玛歌。盡管并發(fā)編程讓我們能更加充分的利用IO資源昧港,但是也給我們帶來了新的問題。
當(dāng)多個進(jìn)程使用同一份數(shù)據(jù)資源的時候支子,就會引發(fā)數(shù)據(jù)安全或順序混亂問題创肥。
多進(jìn)程搶占輸出資源
import osimport timeimport randomfrom multiprocessing import Processdef work(n):
? ? print('%s: %s is running' %(n,os.getpid()))
? ? time.sleep(random.random())
? ? print('%s:%s is done' %(n,os.getpid()))if __name__ == '__main__':
? ? for i in range(3):
? ? ? ? p=Process(target=work,args=(i,))
? ? ? ? p.start()
使用鎖維護(hù)執(zhí)行順序
# 由并發(fā)變成了串行,犧牲了運(yùn)行效率,但避免了競爭import osimport timeimport randomfrom multiprocessing import Process,Lockdef work(lock,n):
? ? lock.acquire()
? ? print('%s: %s is running' % (n, os.getpid()))
? ? time.sleep(random.random())
? ? print('%s: %s is done' % (n, os.getpid()))
? ? lock.release()if __name__ == '__main__':
? ? lock=Lock()
? ? for i in range(3):
? ? ? ? p=Process(target=work,args=(lock,i))
? ? ? ? p.start()
上面這種情況雖然使用加鎖的形式實(shí)現(xiàn)了順序的執(zhí)行,但是程序又重新變成串行了值朋,這樣確實(shí)會浪費(fèi)了時間叹侄,卻保證了數(shù)據(jù)的安全。
接下來昨登,我們以模擬搶票為例趾代,來看看數(shù)據(jù)安全的重要性。?
多進(jìn)程同時搶購余票
#文件db的內(nèi)容為:{"count":1}
#注意一定要用雙引號丰辣,不然json無法識別
#并發(fā)運(yùn)行撒强,效率高禽捆,但競爭寫同一文件,數(shù)據(jù)寫入錯亂from multiprocessing import Process,Lockimport time,json,randomdef search():
? ? dic=json.load(open('db'))
? ? print('\033[43m剩余票數(shù)%s\033[0m' %dic['count'])def get():
? ? dic=json.load(open('db'))
? ? time.sleep(0.1) #模擬讀數(shù)據(jù)的網(wǎng)絡(luò)延遲? ? if dic['count'] >0:
? ? ? ? dic['count']-=1
? ? ? ? time.sleep(0.2) #模擬寫數(shù)據(jù)的網(wǎng)絡(luò)延遲? ? ? ? json.dump(dic,open('db','w'))
? ? ? ? print('\033[43m購票成功\033[0m')def task():
? ? search()
? ? get()if __name__ == '__main__':
? ? for i in range(100): #模擬并發(fā)100個客戶端搶票? ? ? ? p=Process(target=task)
? ? ? ? p.start()
使用鎖來保證數(shù)據(jù)安全
#文件db的內(nèi)容為:{"count":5}
#注意一定要用雙引號飘哨,不然json無法識別
#并發(fā)運(yùn)行胚想,效率高,但競爭寫同一文件芽隆,數(shù)據(jù)寫入錯亂from multiprocessing import Process,Lockimport time,json,randomdef search():
? ? dic=json.load(open('db'))
? ? print('\033[43m剩余票數(shù)%s\033[0m' %dic['count'])def get():
? ? dic=json.load(open('db'))
? ? time.sleep(random.random()) #模擬讀數(shù)據(jù)的網(wǎng)絡(luò)延遲? ? if dic['count'] >0:
? ? ? ? dic['count']-=1
? ? ? ? time.sleep(random.random()) #模擬寫數(shù)據(jù)的網(wǎng)絡(luò)延遲? ? ? ? json.dump(dic,open('db','w'))
? ? ? ? print('\033[32m購票成功\033[0m')
? ? else:
? ? ? ? print('\033[31m購票失敗\033[0m')def task(lock):
? ? search()
? ? lock.acquire()
? ? get()
? ? lock.release()if __name__ == '__main__':
? ? lock = Lock()
? ? for i in range(100): #模擬并發(fā)100個客戶端搶票? ? ? ? p=Process(target=task,args=(lock,))
? ? ? ? p.start()
#加鎖可以保證多個進(jìn)程修改同一塊數(shù)據(jù)時,同一時間只能有一個任務(wù)可以進(jìn)行修改臼闻,即串行的修改,沒錯乓搬,速度是慢了,但犧牲了速度卻保證了數(shù)據(jù)安全江掩。雖然可以用文件共享數(shù)據(jù)實(shí)現(xiàn)進(jìn)程間通信环形,但問題是:
1.效率低(共享數(shù)據(jù)基于文件,而文件是硬盤上的數(shù)據(jù))
2.需要自己加鎖處理#因此我們最好找尋一種解決方案能夠兼顧:1火本、效率高(多個進(jìn)程共享一塊內(nèi)存的數(shù)據(jù))2、幫我們處理好鎖問題刃鳄。這就是mutiprocessing模塊為我們提供的基于消息的IPC通信機(jī)制:隊(duì)列和管道挪鹏。隊(duì)列和管道都是將數(shù)據(jù)存放于內(nèi)存中
隊(duì)列又是基于(管道+鎖)實(shí)現(xiàn)的讨盒,可以讓我們從復(fù)雜的鎖問題中解脫出來,
我們應(yīng)該盡量避免使用共享數(shù)據(jù)遂鹊,盡可能使用消息傳遞和隊(duì)列,避免處理復(fù)雜的同步和鎖問題舟陆,而且在進(jìn)程數(shù)目增多時,往往可以獲得更好的可獲展性。
信號量 —— multiprocess.Semaphore(了解)
信號量介紹Semaphore
互斥鎖同時只允許一個線程更改數(shù)據(jù)勾扭,而信號量Semaphore是同時允許一定數(shù)量的線程更改數(shù)據(jù) 桅滋。
假設(shè)商場里有4個迷你唱吧丐谋,所以同時可以進(jìn)去4個人泌豆,如果來了第五個人就要在外面等待蔬浙,等到有人出來才能再進(jìn)去玩畴博。
實(shí)現(xiàn):
信號量同步基于內(nèi)部計(jì)數(shù)器,每調(diào)用一次acquire()亮隙,計(jì)數(shù)器減1棚饵;每調(diào)用一次release()硼砰,計(jì)數(shù)器加1.當(dāng)計(jì)數(shù)器為0時,acquire()調(diào)用被阻塞。這是迪科斯徹(Dijkstra)信號量概念P()和V()的Python實(shí)現(xiàn)血公。信號量同步機(jī)制適用于訪問像服務(wù)器這樣的有限資源够滑。
信號量與進(jìn)程池的概念很像彰触,但是要區(qū)分開分蓖,信號量涉及到加鎖的概念
例子
from multiprocessing import Process,Semaphoreimport time,randomdef go_ktv(sem,user):
? ? sem.acquire()
? ? print('%s 占到一間ktv小屋' %user)
? ? time.sleep(random.randint(0,3)) #模擬每個人在ktv中待的時間不同? ? sem.release()if __name__ == '__main__':
? ? sem=Semaphore(4)
? ? p_l=[]
? ? for i in range(13):
? ? ? ? p=Process(target=go_ktv,args=(sem,'user%s' %i,))
? ? ? ? p.start()
? ? ? ? p_l.append(p)
? ? for i in p_l:
? ? ? ? i.join()
? ? print('============》')
事件 —— multiprocess.Event(了解)
事件介紹
python線程的事件用于主線程控制其他線程的執(zhí)行,事件主要提供了三個方法 set、wait昧辽、clear。
? ? 事件處理的機(jī)制:全局定義了一個“Flag”,如果“Flag”值為 False,那么當(dāng)程序執(zhí)行 event.wait 方法時就會阻塞,如果“Flag”值為True愉粤,那么event.wait 方法時便不再阻塞绷雏。
clear:將“Flag”設(shè)置為False
set:將“Flag”設(shè)置為True
紅綠燈實(shí)例
from multiprocessing import Process, Eventimport time, randomdef car(e, n):
? ? while True:
? ? ? ? if not e.is_set():? # 進(jìn)程剛開啟兴猩,is_set()的值是Flase讨勤,模擬信號燈為紅色? ? ? ? ? ? print('\033[31m紅燈亮\033[0m,car%s等著' % n)
? ? ? ? ? ? e.wait()? ? # 阻塞屉来,等待is_set()的值變成True蝶桶,模擬信號燈為綠色? ? ? ? ? ? print('\033[32m車%s 看見綠燈亮了\033[0m' % n)
? ? ? ? ? ? time.sleep(random.randint(3, 6))
? ? ? ? ? ? if not e.is_set():? #如果is_set()的值是Flase恢共,也就是紅燈,仍然回到while語句開始? ? ? ? ? ? ? ? continue? ? ? ? ? ? print('車開遠(yuǎn)了,car', n)
? ? ? ? ? ? breakdef police_car(e, n):
? ? while True:
? ? ? ? if not e.is_set():# 進(jìn)程剛開啟,is_set()的值是Flase,模擬信號燈為紅色? ? ? ? ? ? print('\033[31m紅燈亮\033[0m规阀,car%s等著' % n)
? ? ? ? ? ? e.wait(0.1) # 阻塞,等待設(shè)置等待時間,等待0.1s之后沒有等到綠燈就闖紅燈走了? ? ? ? ? ? if not e.is_set():
? ? ? ? ? ? ? ? print('\033[33m紅燈,警車先走\(yùn)033[0m,car %s' % n)
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? print('\033[33;46m綠燈,警車走\(yùn)033[0m贰军,car %s' % n)
? ? ? ? breakdef traffic_lights(e, inverval):
? ? while True:
? ? ? ? time.sleep(inverval)
? ? ? ? if e.is_set():
? ? ? ? ? ? print('######', e.is_set())
? ? ? ? ? ? e.clear()? # ---->將is_set()的值設(shè)置為False? ? ? ? else:
? ? ? ? ? ? e.set()? ? # ---->將is_set()的值設(shè)置為True? ? ? ? ? ? print('***********',e.is_set())if __name__ == '__main__':
? ? e = Event()
? ? for i in range(10):
? ? ? ? p=Process(target=car,args=(e,i,))? # 創(chuàng)建是個進(jìn)程控制10輛車? ? ? ? p.start()
? ? for i in range(5):
? ? ? ? p = Process(target=police_car, args=(e, i,))? # 創(chuàng)建5個進(jìn)程控制5輛警車? ? ? ? p.start()
? ? t = Process(target=traffic_lights, args=(e, 10))? # 創(chuàng)建一個進(jìn)程控制紅綠燈? ? t.start()
? ? print('============》')