·程序執行到os.fork()時,操作系統會創建一個新的進程(子進程),然后復制父進程的所有信息到子進程中
·然后父進程和子進程都會從fork()函數中得到一個返回值,在子進程中這個值一定是0,而父進程中是子進程的id號
普通的函數調用,調用一次,返回一次,但是fork()調用一次,返回兩次,因為操作系統自動把當前進程(稱為父進程)復制了一份(稱為子進程),然后,分別在父進程和子進程內返回。
子進程永遠返回0,而父進程返回子進程的ID。
這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getppid()就可以拿到父進程的ID。
使用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值;
結果
從結果不難看出主進程和子進程在爭奪CPU的使用權。
進程中的join
進程中的守護進程
結果:當pc2注冊掉之后,可以看出當非守護進程(這里就是主進程)結束后,守護進程也結束。整個進程結束
當有三個進程的時候,爭奪CPU的使用權
當兩個join放在一起時,Process中的一些小判斷:
1.1.1進程的創建-Process子類
#因為Process類本身也有__init__方法,這個子類相當于重寫了這個方法,
#但這樣就會帶來一個問題,我們并沒有完全的初始化一個Process類,所以就不能使用從這個類繼承的一些方法和屬性,
#最好的方法就是將繼承類本身傳遞給Process.__init__方法,完成這些初始化操作
兩種方式的對比:
1、方法
2、繼承類
繼承類是以面向對象考慮這個事的,所以業務邏輯復雜,建議使用繼承類,更好理解
1.1.1進程池Pool
當需要創建的子進程數量不多時,可以直接利用multiprocessing中的Process動態成生多個進程,但如果是上百甚至上千個目標,手動的去創建進程的工作量巨大,此時就可以用到multiprocessing模塊提供的Pool方法。
初始化Pool時,可以指定一個最大進程數,當有新的請求提交到Pool中時,如果池還沒有滿,那么就會創建一個新的進程用來執行該請求;但如果池中的進程數已經達到指定的最大值,那么該請求就會等待,直到池中有進程結束,才會創建新的進程來執行,請看下面的實例:異步執行:
結果如下:
判斷任務的執行是否是有順序的:
結果
apply堵塞式
結果如下:
multiprocessing.Pool常用函數解析:
·apply_async(func[,?args[,?kwds]]):使用非阻塞方式調用func(并行執行,堵塞方式必須等待上一個進程退出才能執行下一個進程),args為傳遞給func的參數列表,kwds為傳遞給func的關鍵字參數列表;
·apply(func[,?args[,?kwds]]):使用阻塞方式調用func
·close():關閉Pool,使其不再接受新的任務;
·terminate():不管任務是否完成,立即終止;
·join():主進程阻塞,等待子進程的退出,?必須在close或terminate之后使用;
1.1.1進程間通信-Queue
Process之間有時需要通信,操作系統提供了很多機制來實現進程間的通信。
是否可以使用多進程修改全局變量來實現進程之間的通信呢??給出代碼如下:
初始化Queue()對象時(例如:q=Queue()),若括號中沒有指定最大可接收的消息數量,或數量為負值,那么就代表可接受的消息數量沒有上限(直到內存的盡頭);
·Queue.qsize():返回當前隊列包含的消息數量;
·Queue.empty():如果隊列為空,返回True,反之False;
·Queue.full():如果隊列滿了,返回True,反之False;
·Queue.get([block[,?timeout]]):獲取隊列中的一條消息,然后將其從列隊中移除,block默認值為True;
1)如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果為空,此時程序將被阻塞(停在讀取狀態),直到從消息列隊讀到消息為止,如果設置了timeout,則會等待timeout秒,若還沒讀取到任何消息,則拋出"Queue.Empty"異常;
2)如果block值為False,消息列隊如果為空,則會立刻拋出"Queue.Empty"異常;
·Queue.get_nowait():相當Queue.get(False);
·Queue.put(item,[block[,?timeout]]):將item消息寫入隊列,block默認值為True;
1)如果block使用默認值,且沒有設置timeout(單位秒),消息列隊如果已經沒有空間可寫入,此時程序將被阻塞(停在寫入狀態),直到從消息列隊騰出空間為止,如果設置了timeout,則會等待timeout秒,若還沒空間,則拋出"Queue.Full"異常;
2)如果block值為False,消息列隊如果沒有空間可寫入,則會立刻拋出"Queue.Full"異常;
·Queue.put_nowait(item):相當Queue.put(item,?False);
2.?Queue實例
我們以Queue為例,在父進程中創建兩個子進程,一個往Queue里寫數據,一個從Queue里讀數據:
結果如下:
進程池中的Queue
如果要使用Pool創建進程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否則會得到一條如下的錯誤信息:RuntimeError:?Queue?objects?should?only?be?shared?between?processes?through?inheritance
.結果:從結果也可以看出只有寫結束才能進入讀
使用異步時候,當寫的時候休眠,然后執行reader此時阻塞后任務直接結束。