在瞭解其餘概念以前,咱們首先要了解進程的幾個狀態。在程序運行的過程當中,因爲被操做系統的調度算法控制,程序會進入幾個狀態:就緒,運行和阻塞。
(3)阻塞(Blocked)狀態正在執行的進程,因爲等待某個事件發生而沒法執行時,便放棄處理機而處於阻塞狀態。引發進程阻塞的事件可有多種,例如,等待I/O完成、申請緩衝區不能知足、等待信件(信號)等。
所謂同步就是一個任務的完成須要依賴另一個任務時,只有等待被依賴的任務完成後,依賴的任務才能算完成,這是一種可靠的任務序列。要麼成功都成功,失敗都失敗,兩個任務的狀態能夠保持一致。其實就是一個程序結束才執行另一個程序,串行的,不必定兩個程序就有依賴關係。
所謂異步是不須要等待被依賴的任務完成,只是通知被依賴的任務要完成什麼工做,依賴的任務也當即執行,只要本身完成了整個任務就算完成了。至於被依賴的任務最終是否真正完成,依賴它的任務沒法肯定,因此它是不可靠的任務序列。
若是在排隊取餐的人採用的是異步的方式去等待消息被觸發(通知),也就是領了一張小紙條,假如在這段時間裏他不能作其它的事情,就在那坐着等着,不能玩遊戲等,那麼很顯然,這我的被阻塞在了這個等待的操做上面;
想象一下你一邊打着電話一邊還須要擡頭看到底隊伍排到你了沒有,若是把打電話和觀察排隊的位置當作是程序的兩個操做的話,這個程序須要在這兩種不一樣的行爲之間來回的切換,效率可想而知是低下的。
好比說,這我的忽然發覺本身煙癮犯了,須要出去抽根菸,因而他告訴點餐員說,排到我這個號碼的時候麻煩到外面通知我一下,那麼他就沒有被阻塞在這個等待的操做上面,天然這個就是異步+非阻塞的方式了。
不少人會把同步和阻塞混淆,是由於不少時候同步操做會以阻塞的形式表現出來,一樣的,不少人也會把異步和非阻塞混淆,由於異步操做通常都不會在真正的IO操做處被阻塞。
但凡是硬件,都須要有操做系統去管理,只要有操做系統,就有進程的概念,就須要有建立進程的方式,一些操做系統只爲一個應用程序設計,好比微波爐中的控制器,一旦啓動微波爐,全部的進程都已經存在。
1. 系統初始化(查看進程linux中用ps命令,windows中用任務管理器,前臺進程負責與用戶交互,後臺運行的進程與用戶無關,運行在後臺而且只在須要時才喚醒的進程,稱爲守護進程,如電子郵件、web頁面、新聞、打印)
2. 一個進程在運行過程當中開啓了子進程(如nginx開啓多進程,os.fork,subprocess.Popen等)
3. 用戶的交互式請求,而建立一個新進程(如用戶雙擊暴風影音)
4. 一個批處理做業的初始化(只在大型機的批處理系統中應用)
1. 在UNIX中該系統調用是:fork,fork會建立一個與父進程如出一轍的副本,兩者有相同的存儲映像、一樣的環境字符串和一樣的打開文件(在shell解釋器進程中,執行一個命令就會建立一個子進程)
2. 在windows中該系統調用是:CreateProcess,CreateProcess既處理進程的建立,也負責把正確的程序裝入新進程。
1.相同的是:進程建立後,父進程和子進程有各自不一樣的地址空間(多道技術要求物理層面實現進程之間內存的隔離),任何一個進程的在其地址空間中的修改都不會影響到另一個進程。
2.不一樣的是:在UNIX中,子進程的初始地址空間是父進程的一個副本,提示:子進程和父進程是能夠有隻讀的共享內存區的。可是對於windows系統來講,從一開始父進程與子進程的地址空間就是不一樣的。
1. 正常退出(自願,如用戶點擊交互式頁面的叉號,或程序執行完畢調用發起系統調用正常退出,在linux中用exit,在windows中用ExitProcess)
2. 出錯退出(自願,python a.py中a.py不存在)
3. 嚴重錯誤(非自願,執行非法指令,如引用不存在的內存,1/0等,能夠捕捉異常,try...except...)
4. 被其餘進程殺死(非自願,如kill -9)
進程併發的實如今於,硬件中斷一個正在運行的進程,把此時進程運行的全部狀態保存下來,爲此,操做系統維護一張表格,即進程表(process table),每一個進程佔用一個進程表項(這些表項也稱爲進程控制塊)
該表存放了進程狀態的重要信息:程序計數器、堆棧指針、內存分配情況、全部打開文件的狀態、賬號和調度信息,以及其餘在進程由運行態轉爲就緒態或阻塞態時,必須保存的信息,從而保證該進程在再次啓動時,就像從未被中斷過同樣。
仔細說來,multiprocess不是一個模塊而是python中一個操做、管理進程的包。 之因此叫multi是取自multiple的多功能的意思,在這個包中幾乎包含了和進程有關的全部子模塊。因爲提供的子模塊很是多,爲了方便你們歸類記憶,我將這部分大體分爲四個部分:建立進程部分,進程同步部分,進程池部分,進程之間數據共享。重點強調:進程沒有任何共享狀態,進程修改的數據,改動僅限於該進程內,可是經過一些特殊的方法,能夠實現進程之間數據的共享。
1.process模塊介紹
process模塊是一個建立進程的模塊,藉助這個模塊,就能夠完成進程的建立。這個模塊幫咱們建立進程
From Multiprocessing import Process
主進程 當前運行文件
父進程子進程
查看進程ID,
查看父進程 getppid() pp:parent process
查看本身進程的:getpid() p:process
# 兩種傳參方式:
def func1(n):
print('>>>', n)
if __name__ == '__main__':
# p1 = Process(target=func1,args=(1,))
p1 = Process(target=func1, kwargs={'n': 1})
p1.start()
print('主進程結束')
咱們先寫一個程序來看看:
#當前文件名稱爲test.py
from multiprocessing import Process
def func():
print(12345)
if __name__ == '__main__': # windows 下才須要寫這個,這和系統建立進程的機制有關係,不用深究,記着windows下要寫就好啦
# 首先我運行當前這個test.py文件,運行這個文件的程序,那麼就產生了進程,這個進程咱們稱爲主進程
p = Process(target=func, ) # 將函數註冊到一個進程中,p是一個進程對象,此時尚未啓動進程,只是建立了一個進程對象。而且func是不加括號的,由於加上括號這個函數就直接運行了對吧。
p.start() # 告訴操做系統,給我開啓一個進程,func這個函數就被咱們新開的這個進程執行了,而這個進程是我主進程運行過程當中建立出來的,
# 因此稱這個新建立的進程爲主進程的子進程,而主進程又能夠稱爲這個新進程的父進程。
# 而這個子進程中執行的程序,至關於將如今這個test.py文件中的程序copy到一個你看不到的python文件中去執行了,就至關於當前這個文件,被另一個py文件import過去並執行了。
# start並非直接就去執行了,咱們知道進程有三個狀態,進程會進入進程的三個狀態,就緒,(被調度,也就是時間片切/換到它的時候)執行,阻塞,而且在這個三個狀態之間不斷的轉換,等待cpu執行時間片到了。
print('*' * 10) # 這是主進程的程序,上面開啓的子進程的程序是和主進程的程序同時運行的,咱們稱爲異步
上面說了,咱們經過主進程建立的子進程是異步執行的,那麼咱們就驗證一下,而且看一會兒進程和主進程(也就是父進程)的ID號(講一下pid和ppid,使用pycharm舉例),來看看是不是父子關係。
import os
import time
from multiprocessing import Process
# 父進程:子進程是在父進程的運行過程當中開啓的
# 主進程
def func():
print('子進程', os.getpid())
print('子進程的父進程', os.getppid())
print(123)
time.sleep(10)
if __name__ == '__main__':
print('準備開始其餘進程了')
print('主進程的父進程ID號>>>', os.getppid())
print('主進程的進程ID號>>>', os.getpid())
# 建立一個進程,target:我新建立的這個進程要去執行func1這個函數
p = Process(target=func, )
# 啓動進程
p.start()
print('到這裏結束了')
給子進程傳爺爺的參數
def func(pycharm_id):
print('子進程', os.getpid())
print('子進程的父進程', os.getppid())
print('子進程的爺爺進程的ID(pycharm)', pycharm_id)
print(123)
time.sleep(10)
if __name__ == '__main__':
print('準備開始其餘進程了')
print('主進程的父進程ID號>>>', os.getppid())
pycharm_id = os.getppid()
print('主進程的進程ID號>>>', os.getpid())
# 建立一個進程,target:我新建立的這個進程要去執行func1這個函數
p = Process(target=func, args=(pycharm_id,))
# 啓動進程
p.start()
func(pycharm_id)
print('到這裏結束了')
打開windows下的任務管理器,看pycharm的pid進程號,是咱們上面運行的test.py這個文件主進程的父進程號:
看一個問題,說明linux和windows兩個不一樣的操做系統建立進程的不一樣機制致使的不一樣結果:
import time
import os
from multiprocessing import Process
def func():
print('aaaa')
time.sleep(1)
print('子進程>>',os.getpid())
print('該子進程的父進程>>',os.getppid())
print(12345)
print('太白老司機~~~~') #若是我在這裏加了一個打印,你會發現運行結果中會出現兩次打印出來的太白老司機,由於咱們在主進程中開了一個子進程,子進程中的程序至關於import的主進程中的程序,那麼import的時候會不會執行你import的那個文件的程序啊,前面學的,是會執行的,因此出現了兩次打印
#實際上是由於windows開起進程的機制決定的,在linux下是不存在這個效果的,由於windows使用的是process方法來開啓進程,他就會拿到主進程中的全部程序,而linux下只是去執行我子進程中註冊的那個函數,不會執行別的程序,這也是爲何在windows下要加上執行程序的時候,
要加上if __name__ == '__main__':,不然會出現子進程中運行的時候還開啓子進程,那就出現無限循環的建立進程了,就報錯了
一個進程的生命週期:若是子進程的運行時間長,那麼等到子進程執行結束程序才結束,若是主進程的執行時間長,那麼主進程執行結束程序才結束,實際上咱們在子進程中打印的內容是在主進程的執行結果中看不出來的,可是pycharm幫咱們作了優化,由於它會識別到你這是開的子進程,幫你把子進程中打印的內容打印到了顯示臺上。
若是說一個主進程運行完了以後,咱們把pycharm關了,可是子進程尚未執行結束,那麼子進程還存在嗎?這要看你的進程是如何配置的,若是說咱們沒有配置說我主進程結束,子進程要跟着結束,那麼主進程結束的時候,子進程是不會跟着結束的,他會本身執行完,若是我設定的是主進程結束,子進程必須跟着結束,那麼就不會出現單獨的子進程(孤兒進程)了,具體如何設置,看下面的守護進程的講解。好比說,咱們未來啓動項目的時候,可能經過cmd來啓動,那麼我cmd關閉了你的項目就會關閉嗎,不會的,由於你的項目不能中止對外的服務,對吧。
Process類中參數的介紹:
參數介紹:
1 group參數未使用,值始終爲None
2 target表示調用對象,即子進程要執行的任務
3 args表示調用對象的位置參數元組,args=(1,2,'egon',)
4 kwargs表示調用對象的字典,kwargs={'name':'egon','age':18}
5 name爲子進程的名稱
給要執行的函數傳參數:
def func(x, y):
print(x)
time.sleep(1)
print(y)
if __name__ == '__main__':
p = Process(target=func, args=('姑娘', '來玩啊!')) # 這是func須要接收的參數的傳送方式。
p.start()
print('父進程執行結束!')
#執行結果:
#父進程執行結束!
#姑娘
#來玩啊!
Process類中各方法的介紹:
1 p.start():啓動進程,並調用該子進程中的p.run()
2 p.run():進程啓動時運行的方法,正是它去調用target指定的函數,咱們自定義類的類中必定要實現該方法
3 p.terminate():強制終止進程p,不會進行任何清理操做,若是p建立了子進程,該子進程就成了殭屍進程,使用該方法須要特別當心這種狀況。若是p還保存了一個鎖那麼也將不會被釋放,進而致使死鎖
4 p.is_alive():若是p仍然運行,返回True
5 p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,須要強調的是,p.join只能join住start開啓的進程,而不能join住run開啓的進程
join方法的例子:
讓主進程加上join的地方等待(也就是阻塞住),等待子進程執行完以後,再繼續往下執行個人主進程,好多時候,咱們主進程須要子進程的執行結果,因此必需要等待。join感受就像是將子進程和主進程拼接起來同樣,將異步改成同步執行。
join方法的使用:
# 驗證join方法
def func(x,y):
print(x)
time.sleep(1)
print(y)
if __name__ == '__main__':
p = Process(target=func,args=('姑娘','來玩啊!'))
p.start()
print('我這裏是異步的啊!') #這裏相對於子進程仍是異步的
p.join() #只有在join的地方纔會阻塞住,將子進程和主進程之間的異步改成同步
print('父進程執行結束!')
#打印結果:我這裏是異步的啊!
姑娘
來玩啊!
父進程執行結束!
怎麼樣開啓多個進程呢?for循環。而且我有個需求就是說,全部的子進程異步執行,而後全部的子進程所有執行完以後,我再執行主進程,怎麼搞?看代碼
# for循環在建立進程中的應用
def fun1(n):
time.sleep(1)
print(n)
if __name__ == '__main__':
pro_list = []
for i in range(10):
p1 = Process(target=fun1, args=(i,))
p1.start()
pro_list.append(p1)
for p in pro_list:
p.join()
print('主進程結束')
Process類中自帶封裝的各屬性的介紹
1 p.daemon:默認值爲False,若是設爲True,表明p爲後臺運行的守護進程,當p的父進程終止時,p也隨之終止,而且設定爲True後,p不能建立本身的新進程,必須在p.start()以前設置
2 p.name:進程的名稱
3 p.pid:進程的pid
4 p.exitcode:進程在運行時爲None、若是爲–N,表示被信號N結束(瞭解便可)
5 p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是爲涉及網絡鏈接的底層進程間通訊提供安全性,這類鏈接只有在具備相同的身份驗證鍵時才能成功(瞭解便可)
2.Process類的使用
Windows下寫代碼開啓子進程時,必須寫上if __name__ == ‘__main__’:
注意:在windows中Process()必須放到# if __name__ == '__main__':下
進程的建立第二種方法(繼承)
建立進程第二種方法:
本身定義一個類, 繼承Process類, 必須寫一個run方法, 想傳參數, 自行寫init方法, 而後執行super父類的init方法
class MyProcess(Process):
def __init__(self, n):
super().__init__()
self.n = n
def run(self):
print(123)
print('你看看n>>', self.n)
if __name__ == '__main__':
p = MyProcess(100)
p.start() # 給操做系統發送建立進程的指令,子進程建立好以後,要被執行,執行的時候就會執行run方法
print('主進程結束')
進程之間的數據是隔離的:
# 進程之間是空間隔離的,不共享資源
global_num = 100
def func1():
global global_num
global_num = 0
print('子進程全局變量>>>', global_num)
if __name__ == '__main__':
p1 = Process(target=func1, )
p1.start()
time.sleep(1)
print('主進程的全局變量>>>', global_num)
Terminate : 給操做系統發送了一個關閉進程的信號,實際執行是操做系統
is_alive: 看一下你這個進程對象是否還活着
def func1():
time.sleep(2)
print()
print('子進程')
if __name__ == '__main__':
p1 = Process(target=func1, )
p1.start()
p1.terminate() # 給操做系統發了一個關閉p1子進程的信號,關閉進程
time.sleep(1)
print('進程是否還活着:', p1.is_alive())
print(p1.pid)
print('主進程結束')
name與pid
class Piao(Process):
def __init__(self, name):
# self.name=name
# super().__init__() #Process的__init__方法會執行self.name=Piao-1,
# #因此加到這裏,會覆蓋咱們的self.name=name
# 爲咱們開啓的進程設置名字的作法
super().__init__()
self.name = name
def run(self):
print('%s is piaoing' % self.name)
time.sleep(random.randrange(1, 3))
print('%s is piao end' % self.name)
p = Piao('egon')
p.start()
print('開始')
print(p.pid) # 查看pid
殭屍進程與孤兒進程(簡單瞭解 一下就能夠啦)
一:殭屍進程(有害)
殭屍進程:一個進程使用fork建立子進程,若是子進程退出,而父進程並無調用wait或waitpid獲取子進程的狀態信息,那麼子進程的進程描述符仍然保存在系統中。這種進程稱之爲僵死進程。詳解以下
咱們知道在unix/linux中,正常狀況下子進程是經過父進程建立的,子進程在建立新的進程。子進程的結束和父進程的運行是一個異步過程,即父進程永遠沒法預測子進程到底何時結束,若是子進程一結束就馬上回收其所有資源,那麼在父進程內將沒法獲取子進程的狀態信息。
所以,UNⅨ提供了一種機制能夠保證父進程能夠在任意時刻獲取子進程結束時的狀態信息:
一、在每一個進程退出的時候,內核釋放該進程全部的資源,包括打開的文件,佔用的內存等。可是仍然爲其保留必定的信息(包括進程號the process ID,退出狀態the termination status of the process,運行時間the amount of CPU time taken by the process等)
二、直到父進程經過wait / waitpid來取時才釋放. 但這樣就致使了問題,若是進程不調用wait / waitpid的話,那麼保留的那段信息就不會釋放,其進程號就會一直被佔用,可是系統所能使用的進程號是有限的,若是大量的產生僵死進程,將由於沒有可用的進程號而致使系統不能產生新的進程. 此即爲殭屍進程的危害,應當避免。
任何一個子進程(init除外)在exit()以後,並不是立刻就消失掉,而是留下一個稱爲殭屍進程(Zombie)的數據結構,等待父進程處理。這是每一個子進程在結束時都要通過的階段。若是子進程在exit()以後,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態是「Z」。若是父進程能及時 處理,可能用ps命令就來不及看到子進程的殭屍狀態,但這並不等於子進程不通過殭屍狀態。 若是父進程在子進程結束以前退出,則子進程將由init接管。init將會以父進程的身份對殭屍狀態的子進程進行處理。
二:孤兒進程(無害)
孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工做。
孤兒進程是沒有父進程的進程,孤兒進程這個重任就落到了init進程身上,init進程就好像是一個民政局,專門負責處理孤兒進程的善後工做。每當出現一個孤兒進程的時候,內核就把孤 兒進程的父進程設置爲init,而init進程會循環地wait()它的已經退出的子進程。這樣,當一個孤兒進程淒涼地結束了其生命週期的時候,init進程就會表明黨和政府出面處理它的一切善後工做。所以孤兒進程並不會有什麼危害。
咱們來測試一下(建立完子進程後,主進程所在的這個腳本就退出了,當父進程先於子進程結束時,子進程會被init收養,成爲孤兒進程,而非殭屍進程),文件內容
import os
import sys
import time
pid = os.getpid()
ppid = os.getppid()
print 'im father', 'pid', pid, 'ppid', ppid
pid = os.fork()
#執行pid=os.fork()則會生成一個子進程
#返回值pid有兩種值:
# 若是返回的pid值爲0,表示在子進程當中
# 若是返回的pid值>0,表示在父進程當中
if pid > 0:
print 'father died..'
sys.exit(0)
# 保證主線程退出完畢
time.sleep(1)
print 'im child', os.getpid(), os.getppid()
執行文件,輸出結果:
im father pid 32515 ppid 32015
father died..
im child 32516 1
看,子進程已經被pid爲1的init進程接收了,因此殭屍進程在這種狀況下是不存在的,存在只有孤兒進程而已,孤兒進程聲明週期結束天然會被init來銷燬。
三:殭屍進程危害場景:
例若有個進程,它按期的產 生一個子進程,這個子進程須要作的事情不多,作完它該作的事情以後就退出了,所以這個子進程的生命週期很短,可是,父進程只管生成新的子進程,至於子進程 退出以後的事情,則一律漠不關心,這樣,系統運行上一段時間以後,系統中就會存在不少的僵死進程,假若用ps命令查看的話,就會看到不少狀態爲Z的進程。 嚴格地來講,僵死進程並非問題的根源,罪魁禍首是產生出大量僵死進程的那個父進程。所以,當咱們尋求如何消滅系統中大量的僵死進程時,答案就是把產生大 量僵死進程的那個元兇槍斃掉(也就是經過kill發送SIGTERM或者SIGKILL信號啦)。槍斃了元兇進程以後,它產生的僵死進程就變成了孤兒進 程,這些孤兒進程會被init進程接管,init進程會wait()這些孤兒進程,釋放它們佔用的系統進程表中的資源,這樣,這些已經僵死的孤兒進程 就能瞑目而去了。
四:測試
#一、產生殭屍進程的程序test.py內容以下
#coding:utf-8
from multiprocessing import Process
import time,os
def run():
print('子',os.getpid())
if __name__ == '__main__':
p=Process(target=run)
p.start()
print('主',os.getpid())
time.sleep(1000)
#二、在unix或linux系統上執行
[root@vm172-31-0-19 ~]# python3 test.py &
[1] 18652
[root@vm172-31-0-19 ~]# 主 18652
子 18653
[root@vm172-31-0-19 ~]# ps aux |grep Z
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 18653 0.0 0.0 0 0 pts/0 Z 20:02 0:00 [python3] <defunct> #出現殭屍進程
root 18656 0.0 0.0 112648 952 pts/0 S+ 20:02 0:00 grep --color=auto Z
[root@vm172-31-0-19 ~]# top #執行top命令發現1zombie
top - 20:03:42 up 31 min, 3 users, load average: 0.01, 0.06, 0.12
Tasks: 93 total, 2 running, 90 sleeping, 0 stopped, 1 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016884 total, 97184 free, 70848 used, 848852 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 782540 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
root 20 0 29788 1256 988 S 0.3 0.1 0:01.50 elfin
#三、
等待父進程正常結束後會調用wait/waitpid去回收殭屍進程
但若是父進程是一個死循環,永遠不會結束,那麼該殭屍進程就會一直存在,殭屍進程過多,就是有害的
解決方法一:殺死父進程
解決方法二:對開啓的子進程應該記得使用join,join會回收殭屍進程
參考python2源碼註釋
class Process(object):
def join(self, timeout=None):
'''
Wait until child process terminates
'''
assert self._parent_pid == os.getpid(), 'can only join a child process'
assert self._popen is not None, 'can only join a started process'
res = self._popen.wait(timeout)
if res is not None:
_current_process._children.discard(self)
join方法中調用了wait,告訴系統釋放殭屍進程。discard爲從本身的children中剔除
殭屍進程與孤兒進程
3.守護進程
以前咱們講的子進程是不會隨着主進程的結束而結束,子進程所有執行完以後,程序才結束,那麼若是有一天咱們的需求是個人主進程結束了,由我主進程建立的那些子進程必須跟着結束,怎麼辦?守護進程就來了!
主進程建立守護進程
其一:守護進程會在主進程代碼執行結束後就終止
其二:守護進程內沒法再開啓子進程,不然拋出異常:AssertionError: daemonic processes are not allowed to have children
注意:進程之間是互相獨立的,主進程代碼運行結束,守護進程隨即終止
class Myprocess(Process):
def __init__(self, person):
super().__init__()
self.person = person
def run(self):
print(os.getpid(), self.name)
print('%s正在和女主播聊天' % self.person)
time.sleep(3)
if __name__ == '__main__':
p = Myprocess('太白')
p.daemon = True # 必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行
p.start()
# time.sleep(1) # 在sleep時linux下查看進程id對應的進程ps -ef|grep id
print('主')
進程建立的兩種方法
Process()
繼承Process
重寫run方法,傳參數的時候要寫init,可是注意要在init方法中運行父類的init方法
Windows下寫代碼開啓子進程時,必須寫上if __name__ == ‘__main__’:
兩種參數形式
Args=(1,)
Kwargs = {‘n’:1} n必須和執行任務須要的形參相同
驗證空間隔離
對全局變量進行修改
Join
等待子進程運行結束,主進程再繼續往下執行
Terminate : 給操做系統發送了一個關閉進程的信號,實際執行是操做系統
is_alive: 看一下你這個進程對象是否還活着
建立進程的for循環應用
測試了一下三個任務的執行時間
子進程不能input
殭屍進程和孤兒進程
守護進程
1 建立進程的兩種方法
直接使用from multiprocessing import Process
自定義一個類, 繼承Process類, 重寫run方法, 若是須要傳參, 重寫init, 並調用super執行父類的init
2兩種傳參方式:
Args = (1,)元組
Kwargs = {‘n’:1,}
3驗證進程之間是空間隔離的
全局變量
4join等待子進程結束, 而後繼續往下執行,
5驗證了一下併發的執行時間
6for循環在多進程中的應用
7terminate 關閉進程, 可是他只是給操做系統發送一個關閉進程的信號, 實際操做是操做系統關閉的.
8is_alive 查看進程是否還活着
9殭屍進程和孤兒進程
10守護進程
P1.Daemon = True
11子進程不能input
12__name__ == ‘__main__’ windows下
將異步改成同步
同步效率低,可是保證了數據安全 重點
給鑰匙}
with lock(鎖)==========開加還
還鑰匙}
編寫了一個搶票
import time
import random
from multiprocessing import Process, Lock
import json
def get_ticket(i, ticket_lock):
# 全部進程異步執行,到這裏等待,同時再去搶下面的代碼執行
time.sleep(2)
ticket_lock.acquire() # 這裏有個門,只有一我的可以搶到這個鑰匙,加鎖
with open('ticket', 'r') as f:
# 將文件數據load爲字典類型的數據
last_ticket_info = json.load(f)
# 查看一下餘票信息
last_ticket = last_ticket_info['count']
# 若是看到餘票大於0,說明你能夠搶到票
if last_ticket > 0:
# 模擬網絡延遲時間
time.sleep(random.random())
# 搶到一張票就減去
last_ticket -= 1
last_ticket_info['count'] = last_ticket
# 將修改後的餘票寫回文件
with open('ticket', 'w') as f:
# 經過json.dump方法來寫回文件,字符串的形式
json.dump(last_ticket_info, f)
print('%s搶到了' % i)
else:
print("%s沒票" % i)
# 釋放鎖,也就是還鑰匙的操做
ticket_lock.release()
if __name__ == '__main__':
# 建立一個鎖
ticket_lock = Lock()
for i in range(10):
# 將鎖做爲參數傳給每一個進程,由於每一個進程都須要經過所來進行限制,同步
p = Process(target=get_ticket, args=(i, ticket_lock,))
p.start()
0搶到了
1沒票
6沒票
3沒票
2沒票
9沒票
8沒票
5沒票
4沒票
7沒票
#加鎖能夠保證多個進程修改同一塊數據時,同一時間只能有一個任務能夠進行修改,即串行的修改,沒錯,速度是慢了,但犧牲了速度卻保證了數據安全。雖然能夠用文件共享數據實現進程間通訊,但問題是:
1.效率低(共享數據基於文件,而文件是硬盤上的數據)
2.須要本身加鎖處理
#所以咱們最好找尋一種解決方案可以兼顧:一、效率高(多個進程共享一塊內存的數據)二、幫咱們處理好鎖問題。這就是mutiprocessing模塊爲咱們提供的基於消息的IPC通訊機制:隊列和管道。隊列和管道都是將數據存放於內存中
隊列又是基於(管道+鎖)實現的,可讓咱們從複雜的鎖問題中解脫出來,
咱們應該儘可能避免使用共享數據,儘量使用消息傳遞和隊列,避免處理複雜的同步和鎖問題,並且在進程數目增多時,每每能夠得到更好的可獲展性。
IPC通訊機制(瞭解):IPC是intent-Process Communication的縮寫,含義爲進程間通訊或者跨進程通訊,是指兩個進程之間進行數據交換的過程。IPC不是某個系統所獨有的,任何一個操做系統都須要有相應的IPC機制,
好比Windows上能夠經過剪貼板、管道和郵槽等來進行進程間通訊,而Linux上能夠經過命名共享內容、信號量等來進行進程間通訊。Android它也有本身的進程間通訊方式,Android建構在Linux基礎上,繼承了一
部分Linux的通訊方式。