python 進程

一多任務的引入

有很多的場景中的事情是同時進行的,比如開車的時候手和腳共同來駕駛汽車,再比如唱歌跳舞也是同時進行的;

在編程中,一邊唱歌一邊跳舞是如何實現的呢?


·很顯然剛剛的程序并沒有完成唱歌和跳舞同時進行的要求

·如果想要實現“唱歌跳舞”同時進行,那么就需要一個新的方法,叫做:多任務

二多任務的概念

什么叫“多任務”呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽MP3,一邊在用Word趕作業,這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在后臺同時運行著,只是桌面上沒有顯示而已。

現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多任務。由于CPU執行代碼都是順序執行的,那么,單核CPU是怎么執行多任務的呢?

答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒……這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由于CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。

真正的并行執行多任務只能在多核CPU上實現,但是,由于任務數量遠遠多于CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。


三進程的創建-fork

1.進程 vs 程序

編寫完畢的代碼,在沒有運行的時候,稱之為程序

正在運行著的代碼,就成為進程

進程,除了包含代碼以外,還有需要運行的環境等,所以和程序是有區別的。

2.fork

Python的os模塊封裝了常見的系統調用,其中就包括fork,可以在Python程序中輕松創建子進程:

·程序執行到os.fork()時,操作系統會創建一個新的進程(子進程),然后復制父進程的所有信息到子進程中。

·然后父進程和子進程都會從fork()函數中得到一個返回值,在子進程中這個值一定是0,而父進程中是子進程的id號。

在Unix/Linux操作系統中,提供了一個fork()系統函數,它非常特殊。

普通的函數調用,調用一次,返回一次,但是fork()調用一次,返回兩次,因為操作系統自動把當前進程(稱為父進程)復制了一份(稱為子進程),然后,分別在父進程和子進程內返回。

子進程永遠返回0,而父進程返回子進程的ID。

這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getppid()就可以拿到父進程的ID。

四getpid(),getppid()


通過os.pid()和os.ppid()可以獲得程序的子進程的進程號和父進程的進程號

五多進程修改全局變量

·多進程中,每個進程中所有數據(包括全局變量)都各有擁有一份,互不影響。

六多次fork問題

如果在一個程序,有2次的fork函數調用,是否就會有3個進程呢?



父子進程的執行順序

父進程、子進程執行順序沒有規律,完全取決于操作系統的調度算法

七multiprocessing

1 fork只能在Unix/Linux上使用

multiprocessing模塊就是跨平臺版本的多進程模塊,可以在windows系統上使用。

multiprocessing模塊提供了一個Process類來代表一個進程對象,下面的例子演示了啟動一個子進程并等待其結束:


創建子進程時,只需要傳入一個執行函數和函數的參數,創建一個Process實例,用start方法啟動,比fork()還簡單.

·join()方法可以等待子進程結束后再繼續往下運行,通常用于進程間的同步。

Process語法結構如下:

Process([group [, target [, name [, args [, kwargs]]]]])

target:表示這個進程實例所調用對象

args:表示調用對象的位置參數元組

kwargs:表示調用對象的關鍵字參數字典

name:為當前進程實例的別名

group:大多數情況下用不到

Process類常用方法:

·is_alive():判斷進程實例是否還在執行;

·join([timeout]):是否等待進程實例執行結束,或等待多少秒;

·start():啟動進程實例(創建子進程);

·run():如果沒有給定target參數,對這個對象調用start()方法時,就將執行對象中的run()方法;

·terminate():不管任務是否完成,立即終止;

Process類常用屬性:

·name:當前進程實例別名,默認為Process-N,N為從1開始遞增的整數;

·pid:當前進程實例的PID值;

實例1:


實例二:


八進程的創建-Process子類

創建新的進程還能夠使用類的方式,可以自定義一個類,繼承Process類,每次實例化這個類的時候,就等同于實例化一個進程對象,請看下面的實例:


兩種方式的對比:

1、方法

2、繼承類

繼承類是以面向對象考慮這個事的,所以業務邏輯復雜,建議使用繼承類,更好理解

九進程池Pool

當需要創建的子進程數量不多時,可以直接利用multiprocessing中的Process動態成生多個進程,但如果是上百甚至上千個目標,手動的去創建進程的工作量巨大,此時就可以用到multiprocessing模塊提供的Pool方法。

初始化Pool時,可以指定一個最大進程數,當有新的請求提交到Pool中時,如果池還沒有滿,那么就會創建一個新的進程用來執行該請求;但如果池中的進程數已經達到指定的最大值,那么該請求就會等待,直到池中有進程結束,才會創建新的進程來執行,請看下面的實例:



multiprocessing.Pool常用函數解析:

1創建一個進程池

from multiprocessing import Pool

po=Pool(num)

num等于進程池中的進程數量,如果不設置num默認可以放任意數量進程

2為進程池中添加進程(非阻塞式)

po.apply_async(func,(args,),(kwargs,))

使用非阻塞方式調用func,func為創建子進程的函數名(并行執行,阻塞方式必須等待上一個進程退出才能執行下一個進程),args為傳遞給func的參數列表,kwargs為傳遞給func的關鍵字參數列表;

3為進程池中添加進程(阻塞式)

po.apply(func,(args,),(kwargs,))

和非阻塞式最大的區別就是上一個進程退出才能調用下一個進程

4關閉進程池

po.close()

關閉Pool,使其不再接受新的任務

5終止進程

pool.terminate()

不管任務是否完成,立刻終止

6 join

pool.join()

主進程阻塞,等待子進程的退出,必須在close或terminate之后使用

apply阻塞式的實例:


十進程間通信-Queue

Process之間有時需要通信,操作系統提供了很多機制來實現進程間的通信。

1 Queue的使用

可以使用multiprocessing模塊中的Queue來實現多進程之間的數據傳遞,Queue本身是一個消息列隊程序,下面用一個小程序來演示Queue的工作原理:


推薦放消息的方式:判斷隊列是否已滿,再寫入;

推薦讀消息的方式:判斷隊列是否為空,再讀?。?/p>


說明

初始化Queue()對象時(例如q=Queue()),若括號中沒有指定最大可接收的消息數量或數量為負值,那么就代表可接受的消息數量沒有上限。

q=Queue(3)

①q.qsize()

返回當前隊列中包含的消息數量;

②q.empty()

如果隊列為空,返回True,反之False;

q.full()

·如果隊列滿了,返回True,反之False;

④q.get(block,timeout)

·獲取隊列中的一條消息,然后將其從列隊中移除,block默認值為True;

1)如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果為空,此時程序將被阻塞(停在讀取狀態),直到從消息列隊讀到消息為止,如果設置了timeout,則會等待timeout秒,若還沒讀取到任何消息,則拋出"Queue.Empty"異常;

2)如果block值為False,消息列隊如果為空,則會立刻拋出"Queue.Empty"異常;

⑤q.get_nowait()

相當Queue.get(False);

⑥q.put(item,[block[, timeout]])

將item消息寫入隊列,block默認值為True;

1)如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果已經沒有空間可寫入,此時程序將被阻塞(停在寫入狀態),直到從消息列隊騰出空間為止,如果設置了timeout,則會等待timeout秒,若還沒空間,則拋出"Queue.Full"異常;

2)如果block值為False,消息列隊如果沒有空間可寫入,則會立刻拋出"Queue.Full"異常;

⑦q.put_nowait(item)

相當Queue.put(item, False);

2 Queue實例

注意參數的傳遞

我們以Queue為例,在父進程中創建兩個子進程,一個往Queue里寫數據,一個從Queue里讀數據:


3 進程池中的Queue

如果要使用Pool創建進程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否則會得到一條如下的錯誤信息:

RuntimeError: Queue objects should only be shared between processes through inheritance.

下面的實例演示了進程池中的進程如何通信:



最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 要讓Python程序實現多進程(multiprocessing),我們先了解操作系統的相關知識。Unix/Linu...
    壁花燒年閱讀 760評論 0 0
  • 轉自:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348...
    放風箏的小小馬閱讀 225評論 0 2
  • 1.進程 1.1多線程的引入 現實生活中 有很多的場景中的事情是同時進行的,比如開車的時候手和腳共同來駕駛汽車,再...
    TENG書閱讀 513評論 0 0
  • 1.1.1多任務的引入 什么叫“多任務”呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器...
    PythonMaO閱讀 477評論 0 1
  • 一、進程的概念 相信很多同學都聽說過windows、linux,MacOS都是多任務,多用戶的操作系統。那什么是多...
    轉身后的那一回眸閱讀 1,005評論 0 1