多任务编程—多进程
什么是多任務編程?
多任務編程其實和計算機系統內核有關,通過程利用多個計算機內核同時執行程序,以此來提升程序執行的效率。
多任務編程其中包括,多進程、多線程和多協程,這三種多任務編程各有各的優點和缺點,本章就來講一下進程。
首先,什么是進程?
進程就是一次執行的程序,最直接的例子就是我們常用的任務管理器,任務管理器,任務管理中每一個運行中的程序其實都叫進程。
在編程中進程是程序的一種動態過程的描述,進程在執行過程中會占用我們計算機內的資源,但是每個進程都有自己的生命周期(進程在系統內核所存在的時間),生命周期結束后進程會自動銷毀。
每個進程都會有一個獨立的虛擬內存(4g),使其在運行過程中獨立進行。
? 進程的在編程中的作用是為了處理程序并發的一種方式或者說是方法可能會更貼切一點,進程適用于同一程序每次都執行不同的任務,我們稱之為多進程,多進程中分為父進程和子進程。
創建進程的方式一共有兩種:
第一種:導入 import os 模塊 ,利用 os 模塊中的fork( )內建函數來創建子進程,fork( )創建并不難,但是最值得注意的是fork( )這個函數返回值,這個函數相比于其他函數有些特殊,fork( )自身擁有三種返回值,分別是小于0,等于0 和大于0的pid(系統對進程的唯一編號)數字,這三種分對應著不同的處理結果。
小于0 —【創建子進程失敗】
等于0 —【創建子進程成功】
大于 0—【父進程】
在創建過程中一般情況下都會給fork( )的返回值設定一個if判斷語句,以此來確認子進程是否創建成功,至于這個返回值的判斷機制是完全由系統內核經過自動計算后所返回的,這個是機制是不可抗的。下面我們就來創建一個子進程實例,代碼如下:
import os
print('---------分割線-----------')
pid = os.fork()#開始創建子進程
print('fock返回的pid號',pid)
if pid < 0:
print('創建子進程失敗')
elif pid == 0:
print('創建子進程成功')
else:
print('這是父進程') #父進程會先執行
運行結果:
?
?
由此我們可以出所返回值,運行程序中分別生成了一個大于0和一個?等于0數字,說明子進程已經創建成功,因此小于0的值并不會被生成出來。我們可以在已創建的子進程中可以添加自己所需要的內容,讓每個程序都在一個子進程中獨立分開執行,達到并發效果。那么在創建成功后子進程都會包含什么?
子進程會復制父進程中所有的代碼,但是子進程不會執行所復制的所有代碼段,它只會運行所創建的子進程代碼段,父進程的代碼段子進程并不會去運行。(紫色的部分代表分別要執行的代碼段)
如下圖:
?
如果多個進程在同時搶占計算機資源時應該怎么辦,實際上會使計算機對所有進程進行一種快速切換的機制,a進程和b進程同時執行的時候誰先誰后不可定,但是他們一定是交叉運行的。在運行a進程時計算機會把資源 分配給a去使用,但是這時候b進程突然運行,計算機就會把資源在分配給 b去運行,這個過程就是計算機內部的資源分配機制。
但是使用fork( )創建多個子進程就會非常麻煩,子進程中嵌套二級子進程以此類推,這樣不停的去使用if嵌套在結構上是一種很不好的一種體驗,所以在python中有一個更加清晰簡潔的一種創建方式,使用multiprocessing模塊中的Process去創建多個子進程。
?
?
target=‘目標函數'
agrs=(’參數',)#元組傳參
kwargs ={'key':‘value’}#字典傳參
name='命名'?
start( ) #啟動子進程
join( ) #子進程的回收
它的代碼結構非常的清晰,但是相對于fork( ) 它增加了兩個機制,一個是啟動函數strart( ) 和阻塞等待子進程回收機制 join( ),只有所有的子進程全部執行完畢后才會回收,這兩個函數分別控制子進程的開始和結束,其中jion( )這個函數里面可以添加參數(秒),在輸入相應的數字時會自動開始倒計時等待,如果在倒計時結束后join( )機制會執行,但是這個時候join( )子進程并沒有結束,父進程檢測到沒有join阻塞也會執行,這個時候子進程就變為了孤兒進程,孤兒進程在執行完畢后,會在系統中找一個繼父并結束,也就是說這個繼父收養了這個孤兒進程,使其能夠正常結束。
?進程的三態和五態:
三態
就緒態 : 進程具備運行條件,等待系統分配處理器運行
運行態 : 進程占有cpu處于運行的狀態
等待態 : 又稱為阻塞態,睡眠態,指進程暫時不具備運
五態
新建態 : 創建一個進程,獲取資源,直接表現為運行一個程序,或者在程序中創建新的進程
終止態 : 進程執行結束,資源回收過程
進程池:
進程池:一次創建多個進程批量執行多個任務的程序,頻繁大量的任務建議使用進程池 比如一個進程創建后每兩秒就銷毀,但是任務兩很大需要大量重復這個創建銷毀的過程,這種情況下就建議使用進程池去處理 ,打個比方,一輛車上需要搬運200個西瓜才能達到運送標準,在搬運西瓜給貨車的同時有n個人一起去完成這個任務,每次填裝的西瓜數量是n個,可以很快的就將這200個西瓜裝載完畢,大大的 節省了時間成本,所謂的進程池的執行方式就是這個概念。
進程池每次處理事件時不會像普通進程那樣每次處理完事件就會回收,進程池只有處理完所有的事件才會統一回收,再打個比方給大家:
我們創建了四個進程,事件需要執行20次,那么這個進程池每次可以處理1-4這四次事件后并不會回收,等到需要再次處理5-8這四個進程時在開啟,隨后再次關閉,這違背了進程池設計的初衷,進程池內只有執行完這20次的所有事件后才會結束銷毀。
創建進程池使用 Pool函數,形式上和創建進程大同小異,首先需要創建一個事件函數,準備執行,使用Pool(processes=n)來創建n個進程,等待事件的處理,進程池不需要使用start( ) 函數來啟動進程,進程池內有它特有的處理事件的機制,因為在在進程池創建完成后進程就已經是處在一個等待運行的狀態了,將事件放入后,進程會立即執行所放入的事件。
異步處理和同步處理。
異步處理:apply_async( ) 第一個參數為所執行的目標函數,第二個參數為實參,實參的傳遞方式可以選擇元祖傳參和字典傳參,在調用函數和傳遞參數時不需要通過target和args調用, 異步處理會搶占時間片,可以理解成異步處理是一種無序隨機運行。
同步處理:apply( ) 同步處理的調用和傳遞實參的方式和異步處理相同,二者唯一的區別就是一個是有序運行,一種是無序運行。
close( )
進程池中的join( )同樣是等待進程回收,這個之前就已經說過來了,就不在細說,我們重點來看一下這個close( ), close關閉并不是終止進程池運行,而是‘關閉’進程池使其不在接受其他新的事件元素 ,并不是所謂的終止運行。
map函數?
進程池中可以使用map函數對進程池中的數據進行處理,map函數我們在接觸高階函數的時候就已經接觸過了,但是在這里還有一些小小的不同。
以上代碼所示:在進程池中使用map處理元素 ,第一個參數為需要調用 的目標函數,第二個位置是一個迭代器,將迭代對象傳入事件進行處理,因為 l 這個跌迭對象擁有6個元素,但是我們的進程池只有三個進程(每處理一次讓他停一秒,方便我們觀察),所以每秒鐘只能處理三個元素,兩秒后處理完畢,最終將結果打印在終端上。
注:如果在多進程中需要讓第一個子進程完全執行完畢后,在執行第二個子進程,以此類推,這樣的情況可以考慮使用進程所 Lock或Event去設置子進程的阻塞狀態,改變執行效果。
?
轉載于:https://www.cnblogs.com/Jimmy1995/p/9323177.html
總結
- 上一篇: 快影如何提取音频?(快影官方版下载)
- 下一篇: Tomcat权威指南-读书摘要系列6