僵屍與孤兒進程
僵屍進程:父進程的子進程結束的時候父進程沒有wait()情況下子進程會變成僵屍進程
孤兒進程(無害)
一個父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成狀態收集工作。
情況1 無害
父進等着子進程都死,回收僵屍進程。
情況2 無害
父進程死了,子進程活着,都要被init進程接管並且回收。
情況3 有害
父進程一直不死,造成了大量僵屍進程。占用了大量的pid號
pid號是有限的。
解決方案:
最直接的辦法就是殺死父進程 。
Process用法
之前我們簡單介紹了如何用Process實現簡單的多線程
join的用法
join 的作用主要是阻塞住主進程再等待子進程結束,然后再往下執行,(了解的是:內部會待用wait())
join的寫法和start類似,一般用於start之后
from multiprocessing import Process
import time
def foo():
print('進程 start ')
time.sleep(2.3)
print('進程 end ')
if __name__ == '__main__':
p = Process(target=foo)
p.start() #
# 核心需求就是
# time.sleep(5)
p.join() # 阻塞住主進程再等待子進程結束,然后再往下執行,(了解的是:內部會待用wait())
print('主')
join的多進程用法
如果不止一個進程的話,join又會被如何使用呢
from multiprocessing import Process
import time
def foo(x):
print('進程 start ')
time.sleep(x)
print('進程 end ')
if __name__ == '__main__':
p1 = Process(target=foo,args=(1,))
p2 = Process(target=foo,args=(2,))
p3 = Process(target=foo,args=(3,))
start = time.time()
p1.start() #
p2.start() #
p3.start() #
# 核心需求就是
# time.sleep(5)
p3.join() #1s
p1.join() #1s
p2.join() #1s
# 總時長:按照最長的時間計算多一點。
end = time.time()
print(end-start) #3s多 or 6s多 ? 正解:3s多
print('主')
在這種用法中,我們使用了三個進程。我們先將三個進程都啟動,隨后再同時join。我們會發現最后的結果是3秒多一點。其實這三個進程是同時開始的,當第一個進程結束的時候,第二個和第三個進程已經開始一秒多了,所以最后的結果是3秒多
當然,如果我們一個一個的start然后join也是可以達成串行的結果:
from multiprocessing import Process
import time
def foo(x):
print(f'進程{x} start ')
time.sleep(x)
print(f'進程{x} end ')
if __name__ == '__main__':
p1 = Process(target=foo,args=(1,))
p2 = Process(target=foo,args=(2,))
p3 = Process(target=foo,args=(3,))
start = time.time()
p1.start() #
p1.join() #
p2.start() #
p2.join() #
p3.start() #
p3.join() #
# 不如不開,直接穿行調用函數反而快
# foo(1)
# foo(2)
# foo(3)
end = time.time()
print(end-start)
print('主')
只不過這樣的總時長反而高於串行,而且代碼冗余,沒有什么意義
join的多線程用法優化
不知道各位看官有沒有覺得之前的進程每個都要寫一個start和join,看上去很麻煩嗎?如果三個進程還可以接受,那如果更多的進程呢?我們可以依次利用循環對其進行優化
from multiprocessing import Process
import time
def foo(x):
print(f'進程{x} start ')
time.sleep(x)
print(f'進程{x} end ')
if __name__ == '__main__':
start = time.time()
p_list = []
for i in range(1,4):
p = Process(target=foo,args=(i,))
p.start()
p_list.append(p)
print(p_list)
for p in p_list:
p.join()
end = time.time()
print(end-start) #3s多 or 6s多 ? 正解:3s多
print('主')
這樣子代碼的效果 是一樣的,但是看上去就更加的簡單美觀了
Process其他用法
pid(),getpid()和getppid()
其他比較常見的用法是pid(),getpid()和getppid(),他們可以分別用在子進程和父進程中。我們可以直接用代碼來表示用法
from multiprocessing import Process,current_process
import time,os
def task():
print('子進程 start')
print('在子進程中查看自己的pid',current_process().pid) # 在子進程中查看自己的pid
print('在子進程中查看父進程的pid',os.getppid()) #
time.sleep(200)
print('子進程 end')
if __name__ == '__main__':
p = Process(target=task)
p.start()
print('在主進程查看子進程的pid',p.pid) # 一定要寫在 start()之后
print('主進程的pid',os.getpid())
print('主進程的父進程pid',os.getppid())
print('主')
這些用法都是站在當前進程的角度
os.getpid():獲取當前進程的pid
os.getppid():獲取當前進程的父進程的pid
子進程對象.pid:獲取當前進程的子進程pid
name和is_alive
p.name:進程的名稱
p.is_alive():如果p仍然運行,返回True,沒有運行則返回False
from multiprocessing import Process,current_process
import time
def foo():
print('進程 start')
# print('--------------------- ',current_process().name)
time.sleep(2)
print('進程 end')
if __name__ == '__main__':
p = Process(target=foo)
# p2 = Process(target=foo,name='rocky')
p.start()
# p2.start()
print(p.is_alive()) # True
time.sleep(5)
print(p.is_alive()) # 代碼運行完了就算死了 False
print(p.name)
# print(p2.name)
print('主')
terminate()
p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵屍進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那么也將不會被釋放,進而導致死鎖
from multiprocessing import Process,current_process
import time
def foo():
print('進程 start')
# print('--------------------- ',current_process().name)
time.sleep(4294967)
print('進程 end')
if __name__ == '__main__':
p = Process(target=foo)
p.start()
p.terminate() # 給操作系統發了一個請求
print(p.is_alive()) # True
p.join()
print(p.is_alive()) # False
print('主')
如上述代碼,在使用terminate之后程序並不會睡4294967(sleep所能睡的最大的值,不要問我是怎么知道的),而是會直接結束,當然foo()函數里的所有代碼都不會運行,當然,如果你在terminate之前sleep一下的話,那么在執行terminate之前的foo()里的代碼還是會運行的
守護進程
守護--》伴隨
本質也是一個子進程
主進程的代碼執行完畢守護進程直接結束。但是此時主進程可能沒有結束.
from multiprocessing import Process
import time
def foo():
print('守護進程 start')
time.sleep(5)
print('守護進程 end')
if __name__ == '__main__':
p = Process(target=foo)
p.daemon = True # 把這個子進程定義為了守護進程
p.start()
time.sleep(2)
print('主')
守護進程在主進程結束后也會直接結束,上述代碼中 守護進程 end 並不會被執行
from multiprocessing import Process
import time
def foo():
print('守護進程 start')
time.sleep(3)
print('守護進程 end')
def task():
print('子進程 start')
time.sleep(5)
print('子進程 end')
if __name__ == '__main__':
p = Process(target=foo)
p2 = Process(target=task)
p.daemon = True # 把這個子進程定義為了守護進程
p.start()
p2.start()
time.sleep(1)
print('主')
而子進程則不一樣,他並不會隨着主進程結束而結束,所以它會變成孤兒進程