Process用法與進程詳解


僵屍與孤兒進程

僵屍進程:父進程的子進程結束的時候父進程沒有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('主')

而子進程則不一樣,他並不會隨着主進程結束而結束,所以它會變成孤兒進程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM