1. 程式人生 > >python之路-day30-程序

python之路-day30-程序

一、扯點作業系統知識

 

  1、作業系統的作用:

  a、隱藏醜陋複雜的硬體介面,提供良好的的抽象介面

  b、管理、排程程序,並且將多個程序對硬體的競爭變得有序

   

  2、多道技術

  a、生產北京:針對單核,實現併發

  ps:

    現在的主機一般是多核,那麼每個核都會利用多道技術

    有4個cpu,運行於cpu1的某個程式遇到io阻塞,會等到io結束再重新排程,會排程到4個cpu中的任意一個,具體由作業系統排程演算法決定

  b、空間上的複用:如記憶體中同時有多道程式

  c、時間上的複用:複用一個cpu的時間片

    強調:遇到io切,佔用時間過長也切,核心在於切之前將程序的狀態儲存下來,這樣

      才能保證下次切換回來時,能基於上次且走的位置繼續執行。

 

 

二、程序的理論

 

  程序:程序即正在執行的一個過程。程序是對正在執行程式的一個抽象。

第一,程序是一個實體。每一個程序都有它自己的地址空間,一般情況下,包括文字區域(text region)(python的檔案)、資料區域(data region)(python檔案中定義的一些變數資料)和堆疊(stack region)。文字區域儲存處理器執行的程式碼;資料區域儲存變數和程序執行期間使用的動態分配的記憶體;堆疊區域儲存著活動過程呼叫的指令和本地變數。
第二,程序是一個“執行中的程式”。程式是一個沒有生命的實體,只有處理器賦予程式生命時(作業系統執行之),它才能成為一個活動的實體,我們稱其為程序。[
3] 程序是作業系統中最基本、重要的概念。是多道程式系統出現後,為了刻畫系統內部出現的動態情況,描述系統內部各道程式的活動規律引進的一個概念,所有多道程式設計作業系統都建立在程序的基礎上。
程序的概念
動態性:程序的實質是程式在多道程式系統中的一次執行過程,程序是動態產生,動態消亡的。
併發性:任何程序都可以同其他程序一起併發執行
獨立性:程序是一個能獨立執行的基本單位,同時也是系統分配資源和排程的獨立單位;
非同步性:由於程序間的相互制約,使程序具有執行的間斷性,即程序按各自獨立的、不可預知的速度向前推進
結構特徵:程序由程式、資料和程序控制塊三部分組成。
多個不同的程序可以包含相同的程式:一個程式在不同的資料集裡就構成不同的程序,能得到不同的結果;但是執行過程中,程式不能發生改變。
程序的特點
程式是指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。
而程序是程式在處理機上的一次執行過程,它是一個動態的概念。
程式可以作為一種軟體資料長期存在,而程序是有一定生命期的。
程式是永久的,程序是暫時的。
舉例:就像qq一樣,qq是我們安裝在自己電腦上的客戶端程式,其實就是一堆的程式碼檔案,我們不執行qq,那麼他就是一堆程式碼程式,當我們執行qq的時候,這些程式碼執行起來,就成為一個程序了。
程序與程式的區別

 

三、程序的排程

  想要多個程序交替執行,作業系統必須對這些進行排程,這個排程也不是隨即進行的,而是需要遵循一定的法則,由此

  就有了程序的排程演算法。

先來先服務(FCFS)排程演算法是一種最簡單的排程演算法,該演算法既可用於作業排程,也可用於程序排程。FCFS演算法比較有利於長作業(程序),而不利於短作業(程序)。由此可知,本演算法適合於CPU繁忙型作業,而不利於I/O繁忙型的作業(程序)。
先來先服務排程演算法
短作業(程序)優先排程演算法(SJ/PF)是指對短作業或短程序優先排程的演算法,該演算法既可用於作業排程,也可用於程序排程。但其對長作業不利;不能保證緊迫性作業(程序)被及時處理;作業的長短只是被估算出來的。
短作業優先排程演算法
時間片輪轉(Round Robin,RR)法的基本思路是讓每個程序在就緒佇列中的等待時間與享受服務的時間成比例。在時間片輪轉法中,需要將CPU的處理時間分成固定大小的時間片,例如,幾十毫秒至幾百毫秒。如果一個程序在被排程選中之後用完了系統規定的時間片,但又未完成要求的任務,則它自行釋放自己所佔有的CPU而排到就緒佇列的末尾,等待下一次排程。同時,程序排程程式又去排程當前就緒佇列中的第一個程序。
      顯然,輪轉法只能用來排程分配一些可以搶佔的資源。這些可以搶佔的資源可以隨時被剝奪,而且可以將它們再分配給別的程序。CPU是可搶佔資源的一種。但印表機等資源是不可搶佔的。由於作業排程是對除了CPU之外的所有系統硬體資源的分配,其中包含有不可搶佔資源,所以作業排程不使用輪轉法。
在輪轉法中,時間片長度的選取非常重要。首先,時間片長度的選擇會直接影響到系統的開銷和響應時間。如果時間片長度過短,則排程程式搶佔處理機的次數增多。這將使程序上下文切換次數也大大增加,從而加重系統開銷。反過來,如果時間片長度選擇過長,例如,一個時間片能保證就緒佇列中所需執行時間最長的程序能執行完畢,則輪轉法變成了先來先服務法。時間片長度的選擇是根據系統對響應時間的要求和就緒佇列中所允許最大的程序數來確定的。
      在輪轉法中,加入到就緒佇列的程序有3種情況:
      一種是分給它的時間片用完,但程序還未完成,回到就緒佇列的末尾等待下次排程去繼續執行。
      另一種情況是分給該程序的時間片並未用完,只是因為請求I/O或由於程序的互斥與同步關係而被阻塞。當阻塞解除之後再回到就緒佇列。
      第三種情況就是新建立程序進入就緒佇列。
      如果對這些程序區別對待,給予不同的優先順序和時間片從直觀上看,可以進一步改善系統服務質量和效率。例如,我們可把就緒佇列按照程序到達就緒佇列的型別和程序被阻塞時的阻塞原因分成不同的就緒佇列,每個佇列按FCFS原則排列,各佇列之間的程序享有不同的優先順序,但同一佇列內優先順序相同。這樣,當一個程序在執行完它的時間片之後,或從睡眠中被喚醒以及被建立之後,將進入不同的就緒佇列。  

時間片輪轉法
時間片輪轉演算法
前面介紹的各種用作程序排程的演算法都有一定的侷限性。如短程序優先的排程演算法,僅照顧了短程序而忽略了長程序,而且如果並未指明程序的長度,則短程序優先和基於程序長度的搶佔式排程演算法都將無法使用。
而多級反饋佇列排程演算法則不必事先知道各種程序所需的執行時間,而且還可以滿足各種型別程序的需要,因而它是目前被公認的一種較好的程序排程演算法。在採用多級反饋佇列排程演算法的系統中,排程演算法的實施過程如下所述。
(1) 應設定多個就緒佇列,併為各個佇列賦予不同的優先順序。第一個佇列的優先順序最高,第二個佇列次之,其餘各佇列的優先權逐個降低。該演算法賦予各個佇列中程序執行時間片的大小也各不相同,在優先權愈高的佇列中,為每個程序所規定的執行時間片就愈小。例如,第二個佇列的時間片要比第一個佇列的時間片長一倍,……,第i+1個佇列的時間片要比第i個佇列的時間片長一倍。
(2) 當一個新程序進入記憶體後,首先將它放入第一佇列的末尾,按FCFS原則排隊等待排程。當輪到該程序執行時,如它能在該時間片內完成,便可準備撤離系統;如果它在一個時間片結束時尚未完成,排程程式便將該程序轉入第二佇列的末尾,再同樣地按FCFS原則等待排程執行;如果它在第二佇列中執行一個時間片後仍未完成,再依次將它放入第三佇列,……,如此下去,當一個長作業(程序)從第一佇列依次降到第n佇列後,在第n 佇列便採取按時間片輪轉的方式執行。

(3) 僅當第一佇列空閒時,排程程式才排程第二佇列中的程序執行;僅當第1~(i-1)佇列均空時,才會排程第i佇列中的程序執行。如果處理機正在第i佇列中為某程序服務時,又有新程序進入優先權較高的佇列(第1~(i-1)中的任何一個佇列),則此時新程序將搶佔正在執行程序的處理機,即由排程程式把正在執行的程序放回到第i佇列的末尾,把處理機分配給新到的高優先權程序。

多級反饋佇列
最優-多級反饋佇列

 

四、併發與並行

  通過程序之間的排程,也就是程序之間的切換,我們使用者感知到的好像兩個視訊檔案同時在播放,或者音樂和遊戲同時在

進行,那就讓我們來看一下什麼叫做併發和並行

  無論是並行還是併發,在使用者看來都是'同時'執行的,不管是程序還是執行緒,都只是一個任務而已,真正幹活的是cpu,cpu

來做這些任務,而一個cpu同一時刻只能執行一個任務

  併發:就是偽並行,看起來是同試執行的。單個cpu+多道技術就可以實現併發(並行也屬於併發)

你是一個cpu,你同時談了三個女朋友,每一個都可以是一個戀愛任務,你被這三個任務共享要玩出併發戀愛的效果,
應該是你先跟女友1去看電影,看了一會說:不好,我要拉肚子,然後跑去跟第二個女友吃飯,吃了一會說:那啥,我去趟洗手間,然後跑去跟女友3開了個房,然後在你的基友眼裡,你就在和三個女友同時在一起玩。
單cpu,多程序併發舉例

  

  並行:並行即同時執行,只有具備多個cpu才能實現並行

將多個cpu必須成高速公路上的多個車道,程序就好比每個車道上行駛的車輛,並行就是說,大家在自己的車道上行駛,會不影響,同時在開車。這就是並行
多個cpu,多程序並行舉例

  單核下,可以利用多道技術,多個核,每個核也都可以利用多道技術(多道技術是針對單核而言的)

  有四個核,6個任務,這樣同一時間有四個任務被執行,假設分別被分配給了cpu1,cpu2,cpu3,cpu4

  一旦任務1遇到I/O結束了,作業系統會重新呼叫它(須知程序的排程、分配給哪個cpu執行,由作業系統說了算),

 可能被分配給四個cpu中的任意一個區執行

 

五、同步、非同步、阻塞、非阻塞

 

  1、程序的狀態介紹

  在程式執行過程中,由於被作業系統的排程演算法控制,程式會進入幾個狀態:就緒,執行和阻塞

  1)就緒狀態

  當程序已分配到除cpu以外的所有必要資源,只要獲得處理器便可立即執行,這時的程序狀態稱之為就緒狀態。

  2)執行/執行 狀態當程序已獲得處理機,其程式正在處理機上執行,這時的程序狀態稱之為執行狀態

  3)阻塞狀態正在執行的程序,由於等待某個時間發生而無法執行時,便放棄處理機而處於阻塞狀態。引起阻塞的

  事件可有多種,例如,等待I/O完成、申請緩衝區不能滿足、等待新建(訊號)等。

    事件請求:input、sleep、檔案輸入輸出、recv、accept等

    事件發生:sleep、input等完成了

    時間片到了之後就回到就緒狀態,這三個狀態不斷的轉換

 

 

 

   2、同步非同步

    所謂的同步,就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成,這是

  一種可靠的任務序列。要麼成功都成功,失敗都失敗,兩個任務的狀態可以保持一致。其實就是一個程式結束菜執行另外一個程式

  序列的,不一定兩個程式就有依賴關係。

    所謂非同步是不需要等待被依賴的動作完成,只是通知被依賴的任務要完成什麼工作,依賴的任務也立即執行,只要自己完成了

  整個任務就算完成了。至於被依賴的任務最重四否真正完成,依賴他的任務無法確定,所以他是不可靠的任務序列

比如我們去樓下的老家肉餅吃飯,飯點好了,取餐的時候發生了一些同步非同步的事情。
同步:我們都站在隊裡等著取餐,前面有個人點了一份肉餅,後廚做了很久,但是由於同步機制,我們還是要站在隊裡等著前面那個人的肉餅做好取走,我們才往前走一步。
非同步:我們點完餐之後,點餐員給了我們一個取餐號碼,跟你說,你不用在這裡排隊等著,去找個地方坐著玩手機去吧,等飯做好了,我叫你。這種機制(等待別人通知)就是非同步等待訊息通知。在非同步訊息處理中,等待訊息通知者(在這個例子中等著取餐的你)往往註冊一個回撥機制,在所等待的事件被觸發時由觸發機制(點餐員)通過某種機制(喊號,‘250號你的包子好了‘)找到等待該事件的人。
同步非同步舉例

 

   3、阻塞與非阻塞

阻塞和非阻塞這兩個概念與程式(執行緒)等待訊息通知(無所謂同步或者非同步)時的狀態有關。也就是說阻塞與非阻塞主要是程式(執行緒)等待訊息通知時的狀態角度來說的

繼續上面的那個例子,不論是排隊還是使用號碼等待通知,如果在這個等待的過程中,等待者除了等待訊息通知之外不能做其它的事情,那麼該機制就是阻塞的,表現在程式中,也就是該程式一直阻塞在該函式呼叫處不能繼續往下執行。
相反,有的人喜歡在等待取餐的時候一邊打遊戲一邊等待,這樣的狀態就是非阻塞的,因為他(等待者)沒有阻塞在這個訊息通知上,而是一邊做自己的事情一邊等待。阻塞的方法:input、time.sleep,socket中的recv、accept等等。
阻塞與非阻塞

 

   4、同步/非同步 與 阻塞和非阻塞

1、同步阻塞形式
    效率最低。拿上面的例子來說,就是你專心排隊,什麼別的事都不做。

2、非同步阻塞形式
    如果在排隊取餐的人採用的是非同步的方式去等待訊息被觸發(通知),也就是領了一張小紙條,假如在這段時間裡他不能做其它的事情,就在那坐著等著,不能玩遊戲等,那麼很顯然,這個人被阻塞在了這個等待的操作上面;

    非同步操作是可以被阻塞住的,只不過它不是在處理訊息時阻塞,而是在等待訊息通知時被阻塞。

3、同步非阻塞形式
    實際上是效率低下的。

    想象一下你一邊打著電話一邊還需要擡頭看到底隊伍排到你了沒有,如果把打電話和觀察排隊的位置看成是程式的兩個操作的話,這個程式需要在這兩種不同的行為之間來回的切換,效率可想而知是低下的。

4、非同步非阻塞形式
    效率更高,

    因為打電話是你(等待者)的事情,而通知你則是櫃檯(訊息觸發機制)的事情,程式沒有在兩種不同的操作中來回切換。

    比如說,這個人突然發覺自己煙癮犯了,需要出去抽根菸,於是他告訴點餐員說,排到我這個號碼的時候麻煩到外面通知我一下,那麼他就沒有被阻塞在這個等待的操作上面,自然這個就是非同步+非阻塞的方式了。

  很多人會把同步和阻塞混淆,是因為很多時候同

  

 

 六、程序的建立、結束與併發的實現

 

  

1.程序的建立

    但凡是硬體,都需要有作業系統去管理,只要有作業系統,就有程序的概念,就需要有建立程序的方式,一些作業系統只為一個應用程式設計,比如微波爐中的控制器,一旦啟動微波爐,所有的程序都已經存在。

    而對於通用系統(跑很多應用程式),需要有系統執行過程中建立或撤銷程序的能力,主要分為4中形式建立新的程序

      1. 系統初始化(檢視程序linux中用ps命令,windows中用工作管理員,前臺程序負責與使用者互動,後臺執行的程序與使用者無關,執行在後臺並且只在需要時才喚醒的程序,稱為守護程序,如電子郵件、web頁面、新聞、列印)

      2. 一個程序在執行過程中開啟了子程序(如nginx開啟多程序,os.fork,subprocess.Popen等)

      3. 使用者的互動式請求,而建立一個新程序(如使用者雙擊暴風影音)

      4. 一個批處理作業的初始化(只在大型機的批處理系統中應用)

  

    無論哪一種,新程序的建立都是由一個已經存在的程序執行了一個用於建立程序的系統呼叫而建立的:

      1. 在UNIX中該系統呼叫是:fork,fork會建立一個與父程序一模一樣的副本,二者有相同的儲存映像、同樣的環境字串和同樣的開啟檔案(在shell直譯器程序中,執行一個命令就會建立一個子程序)

      2. 在windows中該系統呼叫是:CreateProcess,CreateProcess既處理程序的建立,也負責把正確的程式裝入新程序。

 

    關於建立的子程序,UNIX和windows

      1.相同的是:程序建立後,父程序和子程序有各自不同的地址空間(多道技術要求物理層面實現程序之間記憶體的隔離),任何一個程序的在其地址空間中的修改都不會影響到另外一個程序。

      2.不同的是:在UNIX中,子程序的初始地址空間是父程序的一個副本,提示:子程序和父程序是可以有隻讀的共享記憶體區的。但是對於windows系統來說,從一開始父程序與子程序的地址空間就是不同的。

 

2.程序的結束 

    1. 正常退出(自願,如使用者點選互動式頁面的叉號,或程式執行完畢呼叫發起系統呼叫正常退出,在linux中用exit,在windows中用ExitProcess)

    2. 出錯退出(自願,python a.py中a.py不存在)

    3. 嚴重錯誤(非自願,執行非法指令,如引用不存在的記憶體,1/0等,可以捕捉異常,try...except...)

    4. 被其他程序殺死(非自願,如kill -9)

 3.程序併發的實現(瞭解)

    程序併發的實現在於,硬體中斷一個正在執行的程序,把此時程序執行的所有狀態儲存下來,為此,作業系統維護一張表格,即程序表(process table),每個程序佔用一個程序表項(這些表項也稱為程序控制塊)

 

 

 

 七、mutiprocess 模組

 

  仔細說來,multiprocess不是一個模組而是python中的一個操作,管理程序的包。之所以叫multi是提取自mutilple的多功能的意思,在

這個包中幾乎包含了和程序有關的所有子模組。由於提供的子模組非常多,為了方便大家歸類記憶,我們將這部分大致分為四個部分:

建立程序部分,程序同步部分,程序池部分,程序之間的數共享。重點強調:程序沒有任何共享狀態,程序修改的資料,改動僅限於該程序

內,但是通過一些特殊的方法,可以實現程序之間的資料共享。

 

  1、process模組介紹

    process模組是一個建立程序的模組,藉助這個模組,就可以完成程序的建立。

Process([group [, target [, name [, args [, kwargs]]]]]),由該類例項化得到的物件,表示一個子程序中的任務(尚未啟動)

強調:
1. 需要使用關鍵字的方式來指定引數
2. args指定的為傳給target函式的位置引數,是一個元組形式,必須有逗號

  我們先寫一個 程式來看看:

#當前檔名稱為test.py
# from multiprocessing import Process
#
# def func():
#     print(12345)
#
# if __name__ == '__main__': #windows 下才需要寫這個,這和系統建立程序的機制有關係,不用深究,記著windows下要寫就好啦
#     #首先我運行當前這個test.py檔案,執行這個檔案的程式,那麼就產生了程序,這個程序我們稱為主程序
#
#     p = Process(target=func,) #將函式註冊到一個程序中,p是一個程序物件,此時還沒有啟動程序,只是建立了一個程序物件。並且func是不加括號的,因為加上括號這個函式就直接運行了對吧。
#     p.start() #告訴作業系統,給我開啟一個程序,func這個函式就被我們新開的這個程序執行了,而這個程序是我主程序執行過程中創建出來的,所以稱這個新建立的程序為主程序的子程序,而主程序又可以稱為這個新程序的父程序。
          #而這個子程序中執行的程式,相當於將現在這個test.py檔案中的程式copy到一個你看不到的python檔案中去執行了,就相當於當前這個檔案,被另外一個py檔案import過去並執行了。
          #start並不是直接就去執行了,我們知道程序有三個狀態,程序會進入程序的三個狀態,就緒,(被排程,也就是時間片切換到它的時候)執行,阻塞,並且在這個三個狀態之間不斷的轉換,等待cpu執行時間片到了。
#     print('*' * 10) #這是主程序的程式,上面開啟的子程序的程式是和主程序的程式同時執行的,我們稱為非同步
multi-test

   上面說了,我們通過主程序建立的子程序是非同步執行的,那麼我們就驗證一下,並且看一下字程序和主程序(也就是父程序)的ID

  號(講一下pid和ppid,使用pycharm舉例),看看子程序和父程序之間的關係、

 1 import time
 2 import os
 3 
 4 #os.getpid()  獲取自己程序的ID號
 5 #os.getppid() 獲取自己程序的父程序的ID號
 6 
 7 from multiprocessing import Process
 8 
 9 def func():
10     print('aaaa')
11     time.sleep(1)
12     print('子程序>>',os.getpid())
13     print('該子程序的父程序>>',os.getppid())
14     print(12345)
15 
16 if __name__ == '__main__': 
17     #首先我運行當前這個檔案,執行的這個檔案的程式,那麼就產生了主程序
18 
19     p = Process(target=func,) 
20     p.start() 
21     print('*' * 10) 
22     print('父程序>>',os.getpid())
23     print('父程序的父程序>>',os.getppid())
24 
25 #加上time和程序號給大家看一看結果:
26 #********** 首先打印出來了出程序的程式,然後列印的是子程序的,也就是子程序是非同步執行的,相當於主程序和子程序同時執行著,如果是同步的話,我們先執行的是func(),然後再列印主程序最後的10個*號。
27 #父程序>> 3308
28 #父程序的父程序>> 5916 #我執行的test.py檔案的父程序號,它是pycharm的程序號,看下面的截圖
29 
30 #aaaa
31 #子程序>> 4536
32 #該子程序的父程序>> 3308 #是我主程序的ID號,說明主程序為它的父程序
33 
34 #12345
multi-getpid

    一個程序的生命週期:如果子程序的執行時間長,那麼等到子程序執行結束程式才結束,如果主程序的執行時間長,那麼主程序執行結束程式才結束,實際上我們在子

程序中列印     的內容是在主程序的執行結果中看不出來的,但是pycharm幫我們做了優化,因為它會識別到你這是開的子程序,幫你把子程序中列印的內容列印到了顯示臺上。

    如果說一個主程序執行完了之後,我們把pycharm關了,但是子程序還沒有執行結束,那麼子程序還存在嗎?這要看你的程序是如何配置的,如果說我們沒有配置說我主

程序結    束,子程序要跟著結束,那麼主程序結束的時候,子程序是不會跟著結束的,他會自己執行完,如果我設定的是主程序結束,子程序必須跟著結束,那麼就不會出

現單獨的子程序(孤兒程序)了,具體如何設定,看下面的守護程序的講解。比如說,我們將來啟動專案的時候,可能通過cmd來啟動,那麼我cmd關閉了你的專案就會關閉

嗎,不會的,因為你的專案不能停止對外的服務,對吧。

   

  Process類中的引數介紹: 

引數介紹:
1 group引數未使用,值始終為None
2 target表示呼叫物件,即子程序要執行的任務
3 args表示呼叫物件的位置引數元組,args=(1,2,'egon',)
4 kwargs表示呼叫物件的字典,kwargs={'name':'egon','age':18}
5 name為子程序的名稱

  

   給要執行的函式傳引數:

def func(x,y):
    print(x)
    time.sleep(1)
    print(y)

if __name__ == '__main__':

    p = Process(target=func,args=('姑娘','來玩啊!'))#這是func需要接收的引數的傳送方式。
    p.start()
    print('父程序執行結束!')

#執行結果:
父程序執行結束!
姑娘
來玩啊!
multi-引數

 

  Process類中各方法的介紹:

1 p.start():啟動程序,並呼叫該子程序中的p.run() 
2 p.run():程序啟動時執行的方法,正是它去呼叫target指定的函式,我們自定義類的類中一定要實現該方法  
3 p.terminate():強制終止程序p,不會進行任何清理操作,如果p建立了子程序,該子程序就成了殭屍程序,使用該方法需要特別小心這種情況。如果p還儲存了一個鎖那麼也將不會被釋放,進而導致死鎖
4 p.is_alive():如果p仍然執行,返回True
5 p.join([timeout]):主執行緒等待p終止(強調:是主執行緒處於等的狀態,而p是處於執行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的程序,而不能join住run開啟的程序  

  

   join 方法:

  讓主程序加上join的地方等待(也就是阻塞主),等待子程序執行完之後,再繼續往下執行我的子程序,好多時候,我們主程序

需要子程序的執行結果,所以必須要等待。join感覺就像是將子程序和主程序拼接起來一樣,將非同步改為同步執行

 1 def func(x,y):
 2     print(x)
 3     time.sleep(1)
 4     print(y)
 5 
 6 if __name__ == '__main__':
 7 
 8     p = Process(target=func,args=('姑娘','來玩啊!'))
 9     p.start()
10     print('我這裡是非同步的啊!')  #這裡相對於子程序還是非同步的
11     p.join()  #只有在join的地方才會阻塞住,將子程序和主程序之間的非同步改為同步
12     print('父程序執行結束!')
13 
14 #列印結果:
15 我這裡是非同步的啊!
16 姑娘
17 來玩啊!
18 父程序執行結束!
join方法

 

  2、Process 的使用  

 開啟多程序:

 1 #下面的註釋按照編號去看,別忘啦!
 2 import time
 3 import os
 4 from multiprocessing import Process
 5 
 6 def func(x,y):
 7     print(x)
 8     # time.sleep(1) #程序切換:如果沒有這個時間間隔,那麼你會發現func執行結果是列印一個x然後一個y,再列印一個x一個y,不會出現列印多個x然後列印y的情況,因為兩個列印距離太近了而且執行的也非常快,但是如果你這段程式執行慢的話,你就會發現程序之間的切換了。
 9     print(y)
10 
11 if __name__ == '__main__':
12 
13     p_list= []
14     for i in range(10):
15         p = Process(target=func,args=('姑娘%s'%i,'來玩啊!'))
16         p_list.append(p)
17         p.start()
18 
19     [ap.join() for ap in p_list] #4、這是解決辦法,前提是我們的子程序全部都已經去執行了,那麼我在一次給所有正在執行的子程序加上join,那麼主程序就需要等著所有子程序執行結束才會繼續執行自己的程式了,並且保障了所有子程序是非同步執行的。
20 
21         # p.join() #1、如果加到for迴圈裡面,那麼所有子程序包括父程序就全部變為同步了,因為for迴圈也是主程序的,迴圈第一次的時候,一個程序去執行了,然後這個程序就join住了,那麼for迴圈就不會繼續執行了,等著第一個子程序執行結束才會繼續執行for迴圈去建立第二個子程序。
22         #2、如果我不想這樣的,也就是我想所有的子程序是非同步的,然後所有的子程序執行完了再執行主程序
23     #p.join() #3、如果這樣寫的話,多次執行之後,你會發現會出現主程序的程式比一些子程序先執行完,因為我們p.join()是對最後一個子程序進行了join,也就是說如果這最後一個子程序先於其他子程序執行完,那麼主程序就會去執行,而此時如果還有一些子程序沒有執行完,而主程序執行
24          #完了,那麼就會先列印主程序的內容了,這個cpu排程程序的機制有關係,因為我們的電腦可能只有4個cpu,我的子程序加上住程序有11個,雖然我for迴圈是按順序起程序的,但是作業系統一定會按照順序給你執行你的程序嗎,答案是不會的,作業系統會按照自己的演算法來分配進
25               #程給cpu去執行,這裡也解釋了我們打印出來的子程序中的內容也是沒有固定順序的原因,因為列印結果也需要呼叫cpu,可以理解成程序在爭搶cpu,如果同學你想問這是什麼演算法,這就要去研究作業系統啦。那我們的想所有子程序非同步執行,然後再執行主程序的這個需求怎麼解決啊
26     print('不要錢~~~~~~~~~~~~~~~~!')
View Code

 

   建立程序的第二種方法:

 1 class MyProcess(Process): #自己寫一個類,繼承Process類
 2     #我們通過init方法可以傳引數,如果只寫一個run方法,那麼沒法傳引數,因為建立物件的是傳參就是在init方法裡面,面向物件的時候,我們是不是學過
 3     def __init__(self,person):
 4         super().__init__()
 5         self.person=person
 6     def run(self):
 7         print(os.getpid())
 8         print(self.pid)
 9         print(self.pid)
10         print('%s 正在和女主播聊天' %self.person)
11     # def start(self):
12     #     #如果你非要寫一個start方法,可以這樣寫,並且在run方法前後,可以寫一些其他的邏輯
13     #     self.run()
14 if __name__ == '__main__':
15     p1=MyProcess('Jedan')
16     p2=MyProcess('太白')
17     p3=MyProcess('alexDSB')
18 
19     p1.start() #start內部會自動呼叫run方法
20     p2.start()
21     # p2.run()
22     p3.start()
23 
24 
25     p1.join()
26     p2.join()
27     p3.join()
View Code

 

   程序之間的資料是隔離的:

 1 #我們說程序之間的資料是隔離的,也就是資料不共享,看下面的驗證
 2 from multiprocessing import Process
 3 n=100 #首先我定義了一個全域性變數,在windows系統中應該把全域性變數定義在if __name__ == '__main__'之上就可以了
 4 def work():
 5     global n
 6     n=0
 7     print('子程序內: ',n)
 8 
 9 if __name__ == '__main__':
10     p=Process(target=work)
11     p.start()
12     p.join() #等待子程序執行完畢,如果資料共享的話,我子程序是不是通過global將n改為0了,但是你看列印結果,主程序在子程序執行結束之後,仍然是n=100,子程序n=0,說明子程序對n的修改沒有在主程序中生效,說明什麼?說明他們之間的資料是隔離的,互相不影響的
13     print('主程序內: ',n)
14 
15 #看結果:
16 # 子程序內:  0
17 # 主程序內:  100
程序之間資料隔離

 

  Process物件的其他方法或屬性

 1 from multiprocessing import Process
 2 import time
 3 import random
 4 class Piao(Process):
 5     def __init__(self,name):
 6         # self.name=name
 7         # super().__init__() #Process的__init__方法會執行self.name=Piao-1,
 8         #                    #所以加到這裡,會覆蓋我們的self.name=name
 9 
10         #為我們開啟的程序設定名字的做法
11         super().__init__()
12         self.name=name
13 
14     def run(self):
15         print('%s is piaoing' %self.name)
16         time.sleep(random.randrange(1,3))
17         print('%s is piao end' %self.name)
18 
19 p=Piao('egon')
20 p.start()
21 print('開始')
22 print(p.pid) #檢視pid
process的其他方法

 

   殭屍程序和孤兒程序

  1 參考部落格:http://www.cnblogs.com/Anker/p/3271773.html
  2 
  3 一:殭屍程序(有害)
  4   殭屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序並沒有呼叫wait或waitpid獲取子程序的狀態資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序。詳解如下
  5 
  6 我們知道在unix/linux中,正常情況下子程序是通過父程序建立的,子程序在建立新的程序。子程序的結束和父程序的執行是一個非同步過程,即父程序永遠無法預測子程序到底什麼時候結束,如果子程序一結束就立刻回收其全部資源,那麼在父程序內將無法獲取子程序的狀態資訊。
  7 
  8 因此,UNⅨ提供了一種機制可以保證父程序可以在任意時刻獲取子程序結束時的狀態資訊:
  9 1、在每個程序退出的時候,核心釋放該程序所有的資源,包括開啟的檔案,佔用的記憶體等。但是仍然為其保留一定的資訊(包括程序號the process ID,退出狀態the termination status of the process,執行時間the amount of CPU time taken by the process等)
 10 2、直到父程序通過wait / waitpid來取時才釋放. 但這樣就導致了問題,如果程序不呼叫wait / waitpid的話,那麼保留的那段資訊就不會釋放,其程序號就會一直被佔用,但是系統所能使用的程序號是有限的,如果大量的產生僵死程序,將因為沒有可用的程序號而導致系統不能產生新的程序. 此即為殭屍程序的危害,應當避免。
 11 
 12   任何一個子程序(init除外)在exit()之後,並非馬上就消失掉,而是留下一個稱為殭屍程序(Zombie)的資料結構,等待父程序處理。這是每個子程序在結束時都要經過的階段。如果子程序在exit()之後,父程序沒有來得及處理,這時用ps命令就能看到子程序的狀態是“Z”。如果父程序能及時 處理,可能用ps命令就來不及看到子程序的殭屍狀態,但這並不等於子程序不經過殭屍狀態。  如果父程序在子程序結束之前退出,則子程序將由init接管。init將會以父程序的身份對殭屍狀態的子程序進行處理。
 13 
 14 二:孤兒程序(無害)
 15 
 16   孤兒程序:一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。
 17 
 18   孤兒程序是沒有父程序的程序,孤兒程序這個重任就落到了init程序身上,init程序就好像是一個民政局,專門負責處理孤兒程序的善後工作。每當出現一個孤兒程序的時候,核心就把孤 兒程序的父程序設定為init,而init程序會迴圈地wait()它的已經退出的子程序。這樣,當一個孤兒程序淒涼地結束了其生命週期的時候,init程序就會代表黨和政府出面處理它的一切善後工作。因此孤兒程序並不會有什麼危害。
 19 
 20 我們來測試一下(建立完子程序後,主程序所在的這個指令碼就退出了,當父程序先於子程序結束時,子程序會被init收養,成為孤兒程序,而非殭屍程序),檔案內容
 21 
 22 import os
 23 import sys
 24 import time
 25 
 26 pid = os.getpid()
 27 ppid = os.getppid()
 28 print 'im father', 'pid', pid, 'ppid', ppid
 29 pid = os.fork()
 30 #執行pid=os.fork()則會生成一個子程序
 31 #返回值pid有兩種值:
 32 #    如果返回的pid值為0,表示在子程序當中
 33 #    如果返回的pid值>0,表示在父程序當中
 34 if pid > 0:
 35     print 'father died..'
 36     sys.exit(0)
 37 
 38 # 保證主執行緒退出完畢
 39 time.sleep(1)
 40 print 'im child', os.getpid(), os.getppid()
 41 
 42 執行檔案,輸出結果:
 43 im father pid 32515 ppid 32015
 44 father died..
 45 im child 32516 1
 46 
 47 看,子程序已經被pid為1的init程序接收了,所以殭屍程序在這種情況下是不存在的,存在只有孤兒程序而已,孤兒程序宣告週期結束自然會被init來銷燬。
 48 
 49 
 50 三:殭屍程序危害場景:
 51 
 52   例如有個程序,它定期的產 生一個子程序,這個子程序需要做的事情很少,做完它該做的事情之後就退出了,因此這個子程序的生命週期很短,但是,父程序只管生成新的子程序,至於子程序 退出之後的事情,則一概不聞不問,這樣,系統執行上一段時間之後,系統中就會存在很多的僵死程序,倘若用ps命令檢視的話,就會看到很多狀態為Z的程序。 嚴格地來說,僵死程序並不是問題的根源,罪魁禍首是產生出大量僵死程序的那個父程序。因此,當我們尋求如何消滅系統中大量的僵死程序時,答案就是把產生大 量僵死程序的那個元凶槍斃掉(也就是通過kill傳送SIGTERM或者SIGKILL訊號啦)。槍斃了元凶程序之後,它產生的僵死程序就變成了孤兒進 程,這些孤兒程序會被init程序接管,init程序會wait()這些孤兒程序,釋放它們佔用的系統程序表中的資源,這樣,這些已經僵死的孤兒程序 就能瞑目而去了。
 53 
 54 四:測試
 55 #1、產生殭屍程序的程式test.py內容如下
 56 
 57 #coding:utf-8
 58 from multiprocessing import Process
 59 import time,os
 60 
 61 def run():
 62     print('',os.getpid())
 63