Python的進程及進程池詳解
進程
進程是操作系統(tǒng)分配資源的基本單元,是程序隔離的邊界。
進程和程序
程序只是一組指令的集合,它本身沒有任何運行的含義,它是靜態(tài)的。
進程程序的執(zhí)行實例,是動態(tài)的,有自己的生命周期,有創(chuàng)建有撤銷,存在是暫時的。
進程和程序不是一一對應(yīng)的,一個程序可以對應(yīng)多個進程,一個進程也可以執(zhí)行一個或者多個程序。
我們可以這樣理解:編寫完的代碼,沒有運行時稱為程序,正在運行的代碼,會啟動一個(或多個)進程。

進程的狀態(tài)
在我們的操作系統(tǒng)?作時,任務(wù)數(shù)往往?于cpu核心數(shù),即?定有?些任務(wù)正在執(zhí)?,?另外?些任務(wù)在等待cpu,因此導(dǎo)致了進程有不同的狀態(tài)。

- 就緒狀態(tài):已滿?運?條件,等待cpu執(zhí)?
- 執(zhí)?狀態(tài):cpu正在執(zhí)?
- 等待狀態(tài):等待某些條件滿?,比如?個程序sleep了,此時就處于等待狀態(tài)
Python中的進程
在Python中,進程是通過multiprocessing多進程模塊來創(chuàng)建的,multiprocessing模塊提供了?個Process類來創(chuàng)建進程對象。
創(chuàng)建?進程
Process語法結(jié)構(gòu):
Process(group, target, name, args, kwargs)
group:指定進程組,?多數(shù)情況下?不到target:表示調(diào)用對象,即子進程要執(zhí)行的任務(wù)name:子進程的名稱,可以不設(shè)定args:給target指定的函數(shù)傳遞的參數(shù),以元組的?式傳遞kwargs:給target指定的函數(shù)傳遞命名參數(shù)
Process常用方法
p.start()啟動進程,并調(diào)用該子進程中的p.run()方法p.join(timeout):主進程等待?進程執(zhí)?結(jié)束再結(jié)束,timeout是可選的超時時間is_alive():判斷進程?進程是否還存活p.run()進程啟動時運行的方法,正是它去調(diào)用target指定的函數(shù)p.terminate()?即終??進程
Process創(chuàng)建的實例對象的常?屬性
name:當前進程的別名,默認為Process-N,N為從1開始遞增的整數(shù)
pid:當前進程的pid(進程號)
import multiprocessing
import os
import time
def work(name):
print("子進程work正在運行......")
time.sleep(0.5)
print(name)
# 獲取進程的名稱
print("子進程name", multiprocessing.current_process())
# 獲取進程的pid
print("子進程pid", multiprocessing.current_process().pid, os.getpid())
# 獲取父進程的pid
print("父進程pid", os.getppid())
print("子進程運行結(jié)束......")
if __name__ == '__main__':
print("主進程啟動")
# 獲取進程的名稱
print("主進程name", multiprocessing.current_process())
# 獲取進程的pid
print("主進程pid", multiprocessing.current_process().pid, os.getpid())
# 創(chuàng)建進程
p = multiprocessing.Process(group=None, target=work, args=("tigeriaf", ))
# 啟動進程
p.start()
print("主進程結(jié)束")

通過上述代碼我們發(fā)現(xiàn),multiprocessing.Process幫我們創(chuàng)建一個子進程,并且成功運行,但是我們發(fā)現(xiàn),在子進程還沒執(zhí)行完的時候主進程就已經(jīng)死了,那么這個子進程在主進程結(jié)束后就是一個孤兒進程,那么我們可以讓主進程等待子進程結(jié)束后再結(jié)束嗎?答案是可以的。 那就是通過p.join(),join()的作用是讓主進程等子進程執(zhí)行完再退出。
import multiprocessing
import os
import time
def work(name):
print("子進程work正在運行......")
time.sleep(0.5)
print(name)
# 獲取進程的名稱
print("子進程name", multiprocessing.current_process())
# 獲取進程的pid
print("子進程pid", multiprocessing.current_process().pid, os.getpid())
# 獲取父進程的pid
print("父進程pid", os.getppid())
print("子進程運行結(jié)束......")
if __name__ == '__main__':
print("主進程啟動")
# 獲取進程的名稱
print("主進程name", multiprocessing.current_process())
# 獲取進程的pid
print("主進程pid", multiprocessing.current_process().pid, os.getpid())
# 創(chuàng)建進程
p = multiprocessing.Process(group=None, target=work, args=("tigeriaf", ))
# 啟動進程
p.start()
p.join()
print("主進程結(jié)束")
運行結(jié)果:

可以看出,主進程是在子進程結(jié)束后才結(jié)束的。
全局變量問題
全局變量在多個進程中不共享,進程之間的數(shù)據(jù)是獨立的,默認情況下互不影響。
import multiprocessing
# 定義全局變量
num = 99
def work1():
print("work1正在運行......")
global num # 在函數(shù)內(nèi)部聲明使?全局變量num
num = num + 1 # 對num值進?+1
print("work1 num = {}".format(num))
def work2():
print("work2正在運行......")
print("work2 num = {}".format(num))
if __name__ == '__main__':
# 創(chuàng)建進程p1
p1 = multiprocessing.Process(group=None, target=work1)
# 啟動進程p1
p1.start()
# 創(chuàng)建進程p2
p2 = multiprocessing.Process(group=None, target=work2)
# 啟動進程p2
p2.start()
運行結(jié)果:

從運?結(jié)果可以看出,work1()函數(shù)對全局變量num的修改,在work2中并沒有獲取到,?還是原來的99,所以,進程之間是不夠共享變量的。
守護進程
上面說到,可以使用p.join()讓主進程等待子進程結(jié)束后再結(jié)束,那么可不可以讓子進程在主進程結(jié)束的時候就結(jié)束呢?答案是肯定的。 我們可以使用p.daemon = True或者p2.terminate()進行設(shè)置:
import multiprocessing
import time
def work1():
print("work1正在運行......")
time.sleep(4)
print("work1運行完畢")
def work2():
print("work2正在運行......")
time.sleep(10)
print("work2運行完畢")
if __name__ == '__main__':
# 創(chuàng)建進程p1
p1 = multiprocessing.Process(group=None, target=work1)
# 啟動進程p1
p1.start()
# 創(chuàng)建進程p2
p2 = multiprocessing.Process(group=None, target=work2)
# 設(shè)置p2守護主進程
# 第?種?式
# p2.daemon = True 在start()之前設(shè)置,不然會拋異常
# 啟動進程p2
p2.start()
time.sleep(2)
print("主進程運行完畢!")
# 第?種?式
p2.terminate()
執(zhí)行結(jié)果如下:

由于p2設(shè)置了守護主進程,所以主進程運行完畢后,p2子進程也隨之結(jié)束,work2任務(wù)停止,而work1繼續(xù)運行至結(jié)束。
進程池
當需要創(chuàng)建的?進程數(shù)量不多時, 可以直接利?multiprocessing.Process動態(tài)生成多個進程, 但如果要創(chuàng)建很多進程時,?動創(chuàng)建的話?作量會非常大,此時就可以?到multiprocessing模塊提供的Pool去創(chuàng)建一個進程池。
multiprocessing.Pool常?函數(shù):
apply_async(func, args, kwds):使??阻塞?式調(diào)?func(任務(wù)并?執(zhí)?),args為傳遞給func的參數(shù)列表,kwds為傳遞給func的關(guān)鍵字參數(shù)列表apply(func, args, kwds):使?阻塞?式調(diào)?func,必須等待上?個進程執(zhí)行完任務(wù)后才能執(zhí)?下?個進程,了解即可,幾乎不用close():關(guān)閉Pool,使其不再接受新的任務(wù)terminate():不管任務(wù)是否完成,?即終?join():主進程阻塞,等待?進程的退出,必須在close或terminate之后使?
初始化Pool時,可以指定?個最?進程數(shù),當有新的任務(wù)提交到Pool中時,如果進程池還沒有滿,那么就會創(chuàng)建?個新的進程?來執(zhí)?該任務(wù),但如果進程池已滿(池中的進程數(shù)已經(jīng)達到指定的最?值),那么該任務(wù)就會等待,直到池中有進程結(jié)束才會創(chuàng)建新的進程來執(zhí)?。
from multiprocessing import Pool
import time
def work(i):
print("work'{}'執(zhí)行中......".format(i), multiprocessing.current_process().name, multiprocessing.current_process().pid)
time.sleep(2)
print("work'{}'執(zhí)行完畢......".format(i))
if __name__ == '__main__':
# 創(chuàng)建進程池
# Pool(3) 表示創(chuàng)建容量為3個進程的進程池
pool = Pool(3)
for i in range(10):
# 利?進程池同步執(zhí)?work任務(wù),進程池中的進程會等待上?個進程執(zhí)行完任務(wù)后才能執(zhí)?下?個進程
# pool.apply(work, (i, ))
# 使?異步?式執(zhí)?work任務(wù)
pool.apply_async(work, (i, ))
# 進程池關(guān)閉之后不再接受新的請求
pool.close()
# 等待po中所有子進程結(jié)束,必須放在close()后面, 如果使?異步?式執(zhí)?work任務(wù),主線程不再等待?線程執(zhí)?完畢再退出!
pool.join()
執(zhí)行結(jié)果為:

從結(jié)果我們可以看出,只有3個子進程在執(zhí)行任務(wù),此處我們使用的是異步?式(pool.apply_async(work, (i, )))執(zhí)?work任務(wù),如果是以同步方式(pool.apply(work, (i, )))執(zhí)行,進程池中的進程會等待上?個進程執(zhí)行完任務(wù)后才能執(zhí)?下?個進程。
總結(jié)
本篇只介紹了什么是進程、進程與程序的關(guān)系、進程的創(chuàng)建與使用、創(chuàng)建進程池等,并沒有介紹進程同步及進程通信等,下篇文章將會介紹。
相關(guān)文章
Python中l(wèi)ogging.NullHandler 的使用教程
這篇文章主要介紹了Python中l(wèi)ogging.NullHandler 的使用教程,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下2018-11-11
剖析Python的Tornado框架中session支持的實現(xiàn)代碼
這篇文章主要介紹了剖析Python的Tornado框架中session支持的實現(xiàn)代碼,這樣就可以使用Django等框架中大家所熟悉的session了,需要的朋友可以參考下2015-08-08
使用Python中OpenCV和深度學(xué)習(xí)進行全面嵌套邊緣檢測
這篇文章主要介紹了使用Python中OpenCV和深度學(xué)習(xí)進行全面嵌套邊緣檢測,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Python異步編程之協(xié)程任務(wù)的調(diào)度操作實例分析
這篇文章主要介紹了Python異步編程之協(xié)程任務(wù)的調(diào)度操作,結(jié)合實例形式分析了Python異步編程中協(xié)程任務(wù)的調(diào)度相關(guān)原理、實現(xiàn)方法與操作注意事項,需要的朋友可以參考下2020-02-02

