Python并发编程之多进程(一)
一、什么是進(jìn)程
進(jìn)程:正在進(jìn)行的一個過程或者說一個任務(wù)。而負(fù)責(zé)執(zhí)行任務(wù)則是cpu。
進(jìn)程是資源分配的基本單位
進(jìn)程有:代碼段,數(shù)據(jù)段,進(jìn)程控制塊(PCB)組成
二、進(jìn)程與程序的區(qū)別
程序僅僅只是一堆代碼而已,而進(jìn)程指的是程序的運(yùn)行過程。
舉例:
想象一位有一手好廚藝的計算機(jī)科學(xué)家正在為他的女兒烘制生日蛋糕。
他有做生日蛋糕的食譜,
廚房里有所需的原料:面粉、雞蛋、韭菜,蒜泥等。
在這個比喻中:
? 做蛋糕的食譜就是程序(即用適當(dāng)形式描述的算法)
? 計算機(jī)科學(xué)家就是處理器(cpu)
? 而做蛋糕的各種原料就是輸入數(shù)據(jù)。
進(jìn)程就是廚師閱讀食譜、取來各種原料以及烘制蛋糕等一系列動作的總和。
需要強(qiáng)調(diào)的是:同一個程序執(zhí)行兩次,那也是兩個進(jìn)程,比如打開暴風(fēng)影音,雖然都是同一個軟件,但是一個可以播放蒼井空,一個可以播放飯島愛。
三、并發(fā)與并行
無論是并行還是并發(fā),在用戶看來都是’同時’運(yùn)行的,不管是進(jìn)程還是線程,都只是一個任務(wù)而已,真是干活的是cpu,cpu來做這些任務(wù),而一個cpu同一時刻只能執(zhí)行一個任務(wù)
并發(fā):在同一個時間段內(nèi)多個任務(wù)同時進(jìn)行,偽并行,即看起來是同時運(yùn)行。單個cpu+多道技術(shù)就可以實現(xiàn)并發(fā)(并行也屬于并發(fā))
舉例:
你是一個cpu,你同時談了三個女朋友,每一個都可以是一個戀愛任務(wù),你被這三個任務(wù)共享,要玩出并發(fā)戀愛的效果,應(yīng)該是你先跟女友1去看電影,看了一會說:不好,我要拉肚子,然后跑去跟第二個女友吃飯,吃了一會說:那啥,我去趟洗手間,然后跑去跟女友3開了個房
并行:在同一個時間點(diǎn)上多個任務(wù)同時進(jìn)行,同時運(yùn)行,只有具備多個cpu才能實現(xiàn)并行
單核下,可以利用多道技術(shù),多個核,每個核也都可以利用多道技術(shù)(多道技術(shù)是針對單核而言的)
舉例:
有四個核,六個任務(wù),這樣同一時間有四個任務(wù)被執(zhí)行,假設(shè)分別被分配給了cpu1,cpu2,cpu3,cpu4, 一旦任務(wù)1遇到I/O就被迫中斷執(zhí)行,此時任務(wù)5就拿到cpu1的時間片去執(zhí)行,這就是單核下的多道技術(shù),而一旦任務(wù)1的I/O結(jié)束了,操作系統(tǒng)會重新調(diào)用它(需知進(jìn)程的調(diào)度、分配給哪個cpu運(yùn)行,由操作系統(tǒng)說了算),可能被分配給四個cpu中的任意一個去執(zhí)行
四、同步、異步、阻塞、非阻塞
同步
同步:某一個任務(wù)的執(zhí)行必須依賴于另一個任務(wù)的返回結(jié)果
所謂同步,就是在發(fā)出一個功能調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用就不會返回。按照這個定義,其實絕大多數(shù)函數(shù)都是同步調(diào)用。但是一般而言,我們在說同步、異步的時候,特指那些需要其他部件協(xié)作或者需要一定時間完成的任務(wù)
異步
異步:某一個任務(wù)的執(zhí)行,不需要依賴于另一個任務(wù)的返回,只需要告訴另一個任務(wù)一聲
異步的概念和同步相對。當(dāng)一個異步功能調(diào)用發(fā)出后,調(diào)用者不能立刻得到結(jié)果。當(dāng)該異步功能完成后,通過狀態(tài)、通知或回調(diào)來通知調(diào)用者。
阻塞
阻塞:程序因為類似于IO等待、等待事件等導(dǎo)致無法繼續(xù)執(zhí)行。
阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會被掛起(如遇到io操作)。函數(shù)只有在得到結(jié)果之后才會將阻塞的線程激活。有人也許會把阻塞調(diào)用和同步調(diào)用等同起來,實際上他是不同的。對于同步調(diào)用來說,很多時候當(dāng)前線程還是激活的,只是從邏輯上當(dāng)前函數(shù)沒有返回而已。
非阻塞
程序遇到類似于IO操作時,不再阻塞等待,如果沒有及時的處理IO,就報錯或者跳過等其他操作
非阻塞和阻塞的概念相對應(yīng),指在不能立刻得到結(jié)果之前也會立刻返回,同時該函數(shù)不會阻塞當(dāng)前線程。
五、進(jìn)程的基本狀態(tài)
進(jìn)程的三大基本狀態(tài):
就緒狀態(tài):所有進(jìn)程需要的資源都獲取到了,等待著CPU的調(diào)用
執(zhí)行狀態(tài):獲取到了所有資源包括CPU,進(jìn)程處于運(yùn)行狀態(tài)
阻塞狀態(tài):程停滯不再運(yùn)行,放棄了CPU,進(jìn)程此時處于內(nèi)存里
六、multiprocessing模塊介紹
Python中的多線程無法利用多核優(yōu)勢,如果想要充分地使用多核CPU的資源(os.cpu_count()查看),在Python中大部分情況需要使用多進(jìn)程。Python提供了multiprocessing。
multiprocessing模塊用來開啟子進(jìn)程,并在子進(jìn)程中執(zhí)行我們定制的任務(wù)(比如函數(shù)),該模塊與多線程模塊threading的編程接口類似。
multiprocessing模塊的功能眾多:支持子進(jìn)程、通信和共享數(shù)據(jù)、執(zhí)行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。
需要再次強(qiáng)調(diào)的一點(diǎn)是:與線程不同,進(jìn)程沒有任何共享狀態(tài),進(jìn)程修改的數(shù)據(jù),改動僅限于該進(jìn)程內(nèi)。
七、Process類的介紹
1:創(chuàng)建進(jìn)程的類
Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化得到的對象,表示一個子進(jìn)程中的任務(wù)(尚未啟動)強(qiáng)調(diào): 1. 需要使用關(guān)鍵字的方式來指定參數(shù) 2. args指定的為傳給target函數(shù)的位置參數(shù),是一個元組形式,必須有逗號2:參數(shù)介紹
group參數(shù)未使用,值始終為Nonetarget表示調(diào)用對象,即子進(jìn)程要執(zhí)行的任務(wù)args表示調(diào)用對象的位置參數(shù)元組,args=(1,2,'egon',)kwargs表示調(diào)用對象的字典,kwargs={'name':'egon','age':18}name為子進(jìn)程的名稱3:方法介紹
p.start():啟動進(jìn)程,并調(diào)用該子進(jìn)程中的p.run() p.run():進(jìn)程啟動時運(yùn)行的方法,正是它去調(diào)用target指定的函數(shù),我們自定義類的類中一定要實現(xiàn)該方法 p.terminate():強(qiáng)制終止進(jìn)程p,不會進(jìn)行任何清理操作,如果p創(chuàng)建了子進(jìn)程,該子進(jìn)程就成了僵尸進(jìn)程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那么也將不會被釋放,進(jìn)而導(dǎo)致死鎖p.is_alive():如果p仍然運(yùn)行,返回Truep.join([timeout]):主線程等待p終止(強(qiáng)調(diào):是主線程處于等的狀態(tài),而p是處于運(yùn)行的狀態(tài))。timeout是可選的超時時間,需要強(qiáng)調(diào)的是,p.join只能join住start開啟的進(jìn)程,而不能join住run開啟的進(jìn)程4:屬性介紹
p.daemon:默認(rèn)值為False,如果設(shè)為True,代表p為后臺運(yùn)行的守護(hù)進(jìn)程,當(dāng)p的父進(jìn)程終止時,p也隨之終止,并且設(shè)定為True后,p不能創(chuàng)建自己的新進(jìn)程,必須在p.start()之前設(shè)置p.name:進(jìn)程的名稱p.pid:進(jìn)程的pidp.exitcode:進(jìn)程在運(yùn)行時為None、如果為–N,表示被信號N結(jié)束p.authkey:進(jìn)程的身份驗證鍵,默認(rèn)是由os.urandom()隨機(jī)生成的32字符的字符串。這個鍵的用途是為涉及網(wǎng)絡(luò)連接的底層進(jìn)程間通信提供安全性,這類連接只有在具有相同的身份驗證鍵時才能成功八、Process類的使用
注意:在windows中Process()必須放到# if name == ‘main’:下
創(chuàng)建并開啟子進(jìn)程的兩種方式
from multiprocessing import Process import osdef child_process():print("這是子進(jìn)程{0},父進(jìn)程是{1}".format(os.getpid(), os.getppid()))if __name__ == '__main__':child_p = Process(target=child_process)child_p.start()# child_p.join()print("這是父進(jìn)程{0}".format(os.getpid())) from multiprocessing import Process import osclass ChildProcess(Process):def __init__(self):super(ChildProcess, self).__init__()def run(self):print("這是子進(jìn)程{0},父進(jìn)程是{1}".format(os.getpid(), os.getppid()))if __name__ == '__main__':child_p = ChildProcess()child_p.start()# child_p.join()print("這是父進(jìn)程{0}".format(os.getpid()))2:進(jìn)程之間的內(nèi)存空間是隔離的
from multiprocessing import Process import osnum = 100def chile_process():global numnum = 0print("子進(jìn)程中:{0}".format(num))if __name__ == '__main__':p = Process(target=chile_process)p.start()print("父進(jìn)程中:{0}".format(num))# 父進(jìn)程中:100 # 子進(jìn)程中:03:Process中的join()方法
join():主進(jìn)程等待,等待子進(jìn)程結(jié)束
from multiprocessing import Process import os import timedef child_process():time.sleep(3)print("這是子進(jìn)程")if __name__ == '__main__':p = Process(target=child_process)p.start()# p.join()print("這是主進(jìn)程")# 這是主進(jìn)程 # 這是子進(jìn)程 # 分析:如果不加join那么則是先打印主進(jìn)程中的“這是主進(jìn)程”,然后等待三秒在打印“這是子進(jìn)程”from multiprocessing import Process import os import timedef child_process():time.sleep(3)print("這是子進(jìn)程")if __name__ == '__main__':p = Process(target=child_process)p.start()p.join()print("這是主進(jìn)程") # 這是子進(jìn)程 # 這是主進(jìn)程 # 分析:如果加了join那么主進(jìn)程會等待子進(jìn)程執(zhí)行完之后再執(zhí)行主進(jìn)程,也就是說會先等待三秒然后同時打印出“這是子進(jìn)程”和“這是主進(jìn)程”九、守護(hù)進(jìn)程
守護(hù)進(jìn)程的特點(diǎn):
1:守護(hù)進(jìn)程會在主進(jìn)程執(zhí)行完成后終止
2:設(shè)置了守護(hù)進(jìn)程后,守護(hù)進(jìn)程不能再開啟子進(jìn)程,否則會報異常
--------------------------------------------------------------------注:如果你對python感興趣,我這有個學(xué)習(xí)Python基地,里面有很多學(xué)習(xí)資料,感興趣的+Q群:895817687--------------------------------------------------------------------from multiprocessing import Process import timedef func(name):time.sleep(1)print("我是{0}".format(name))def foo(name):time.sleep(3)print("{0}是誰".format(name))if __name__ == '__main__':p1 = Process(target=func, args=("oldwang",))p2 = Process(target=foo, args=("oldwang",))p1.daemon = True# 一定要在p.start()前設(shè)置,設(shè)置p為守護(hù)進(jìn)程,禁止p創(chuàng)建子進(jìn)程,并且父進(jìn)程代碼執(zhí)行結(jié)束,p即終止運(yùn)行p1.start()p2.start()print("這是主進(jìn)程...")# 執(zhí)行結(jié)果: # 這是主進(jìn)程... # oldwang是誰總結(jié)
以上是生活随笔為你收集整理的Python并发编程之多进程(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用Python递归做个多层次的文件执行
- 下一篇: Python并发编程之多进程(二)