python多進程——multiprocessing.Process

  簡介                               

  multiprocessing是一個使用相似於threading模塊的API支持生成進程的包。該multiprocessing軟件包提供本地和遠程併發。所以,該multiprocessing模塊容許程序員充分利用給定機器上的多個處理器。能夠在Unix和Windows上運行。html

multipleprocessing文檔python

 

Process(group=Nonetarget=Nonename=Noneargs=()kwargs={}*daemon=None)

應該始終使用關鍵字參數調用構造函數程序員

  • group參數永遠爲None,該參數僅用於兼容threading.Thread
  • target應該是一個能夠調用的對象,它會被run()方法調用
  • name是進程名
  • args是target所調用方法的參數
  • kwargs是target所調用方法的關鍵字參數
  • daemon默認爲None,意味着從建立進程中繼承,可設爲True(守護進程)或False(非守護進程)

 

start()

  • 啓動進程,只能調用一次,他會在進程中調用run方法

 

join([timeout])

  • 設主進程爲m,子進程爲s,m中調用s.join():阻塞m,直到s進程結束,timeout是一個正數,它最多會阻塞timeout秒 ,另外,s.join()可調用若干次
  • 一個進程p調用本身進程的join (p.join()) 可能會致使死鎖【本身join本身這種騷操做不會,所以沒實驗】
  • 只能在調用s.start()後調用s.join()

  join可防止產生殭屍進程,文檔中的編程指南中指出: 每次開啓一個新進程,全部未被join的進程會被join(也就是說非守護進程會自動被join),但即使如此也要明確地join啓動的全部進程。 所以若是不手動地join子線程,主進程也會等待子進程停止以後再停止編程

【另外join會影響守護進程的行爲,後面探討】併發

 

(一)Process開啓進程:                          

# -*- coding:utf-8 -*-
import os
from multiprocessing import Process


def func(name):
    print('%s進程%d,父進程%d' % (name, os.getpid(), os.getppid()))


'''
在Windows中,Process開啓進程會再次導入此文件,爲防止導入時再次執行,須要添加
if __name__ == '__main__': 
'''
if __name__ == '__main__':
    func('')
    p = Process(target=func, args=[''])
    p.start()
    p.join()

 結果:異步

主進程16452,父進程21852
子進程28472,父進程16452

 

(二)自定義類繼承Process,重寫run方法                 

  若是子類重寫構造函數,則必須確保它在對進程執行任何其餘操做以前調用構造函數 [Process.__init__() ]。函數

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os


class InheritTest(Process):

    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print('我是子進程%s,進程id=%d,父進程id=%d' % (self.name, os.getpid(), os.getppid()))
        time.sleep(2)


if __name__ == '__main__':
    print('我是主進程, 進程id=%d,父進程id=%d' % (os.getpid(), os.getppid()))
    p = InheritTest('小明')
    p.start()
    p.join()

結果:spa

我是主進程, 進程id=18408,父進程id=21852
我是子進程小明,進程id=22640,父進程id=18408

  若多個進程被join,阻塞時長是他們(包括主進程在這期間的執行時間)中執行時間最長的線程

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time


def short_time():
    print('進程pid={},ppid={}'.format(os.getpid(), os.getppid()))
    time.sleep(2)


def long_time():
    print('進程pid={},ppid={}'.format(os.getpid(), os.getppid()))
    time.sleep(4)


if __name__ == "__main__":
    p1 = Process(target=long_time)
    p2 = Process(target=short_time)
    p3 = Process(target=short_time)
    p4 = Process(target=short_time)

    p1.start()
    p2.start()
    p3.start()
    p4.start()

    print('1號進程阻塞中')
    p1.join()
    print('2號進程阻塞中')
    p2.join()
    print('3號進程阻塞中')
    p3.join()
    print('4號進程阻塞中')
    p4.join()


'''
p1-p4異步執行,p1執行時間最長,那麼p1.join()阻塞完後,其它進程已經執行完了,
p2-p4.join()不阻塞,直接執行
'''

結果:code

1號進程阻塞中
進程pid=26404,ppid=17928
進程pid=17960,ppid=17928
進程pid=15592,ppid=17928
進程pid=8724,ppid=17928
2號進程阻塞中
3號進程阻塞中
4號進程阻塞中

  問1:只能在父進程中join子進程嗎?

  問1的解釋: 在子進程中join其它同級的子進程會拋出異常 AssertionError: can only join a child process,所以只能在父進程中join子進程:

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os


def func1():
    time.sleep(2)
    print('func1 進程pid={} ppid={}'.format(os.getpid(), os.getppid()))


def func2(p1):
    p1.join()
    print('func2 進程pid={} ppid={}'.format(os.getpid(), os.getppid()))


if __name__ == "__main__":
    print('主進程id {}'.format(os.getpid()))
    p1 = Process(target=func1)
    p2 = Process(target=func2, args=[p1])

    p2.start()  # 和下面代碼互換會報另外一個異常
    p1.start()
主進程id 14796
Process Process-2:
Traceback (most recent call last):
...............
AssertionError: can only join a child process
func1 進程pid=30100 ppid=14796

  

run()

  • 表示進程活動的方法。調用target指定的函數,若是有參數,會按順序傳入
  • 自定義進程類要在子類中覆蓋此方法。

【單獨調用run不會開啓子進程】

 

is_alive()

  • 查看進程是否還活着。
  • 粗略地說,從start() 方法返回到子進程終止的那一刻,進程對象處於活動狀態。

 

pid

  • 返回進程ID。在產生該過程以前,這將是 None

 

exitcode

  • 子進程的退出碼,若是進程還沒有停止,返回None,若是是-N,表示被信號N停止

 

# -*- coding:utf-8 -*-
import os
from multiprocessing import Process
import time


def func(name):
    print('%s進程%d,父進程%d' % (name, os.getpid(), os.getppid()))
    if name != '':
        time.sleep(2)


if __name__ == '__main__':
    func('')
    p = Process(target=func, args=[''])
    p.start()
    print('p.pid()', p.pid)
    print('p.is_alive()=', p.is_alive())
    print('p.exitcode=', p.exitcode)
    p.join()
    print('p.exitcode=', p.exitcode)
    print('p.is_alive()=', p.is_alive())

結果

主進程1548,父進程21852
p.pid() 24000
p.is_alive()= True
p.exitcode= None
子進程24000,父進程1548
p.exitcode= 0
p.is_alive()= False

 

daemon

  • 進程的守護進程標誌,一個布爾值。必須在start()調用以前設置 它。
  • 初始值繼承自建立過程。
  • 當進程退出時(更確切地說是該進程的代碼執行完畢後),它會嘗試終止其全部守護子進程。
  • 不容許守護進程建立子進程。不然,守護進程會在進程退出時使這些進程變成孤兒進程。此外,這些不是Unix守護程序或服務,它們是正常進程,若是非守護進程已退出,它們將被終止(不會被join)。

 

(三)開啓一個守護進程:                          

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os


def daemon_func():
    time.sleep(2)
    print('守護進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


if __name__ == "__main__":
    print('主進程id {}'.format(os.getpid()))
    p = Process(target=daemon_func)
    p.daemon = True
    p.start()
    # p.join()
當p.join()註釋後,主進程停止,守護進程也停止,所以輸出:
主進程id 15096
若取消註釋,守護進程被join,主進程會等待此守護進程,輸出爲:
主進程id 10896
守護進程pid=19404,ppid=10896

 

(四) 主進程分別開啓一個守護進程和非守護進程:              

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os


# 守護進程p1執行
def daemon_func():
    time.sleep(1)
    print('daemon_func 進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


# 非守護進程p2執行
def non_daemon_func():
    time.sleep(2)
    print('non_daemon_func 進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


if __name__ == "__main__":
    print('主進程id {}'.format(os.getpid()))

    p1 = Process(target=daemon_func)
    p1.daemon = True
    p2 = Process(target=non_daemon_func)

    p1.start()
    p2.start()
  主進程執行完當即停止守護進程(主進程此時本身沒停止),主進程等待非守護進程,而後停止
所以執行結果是:

主進程id 24588
non_daemon_func 進程pid=3772,ppid=24588   ### 這裏ppid是24588,而不是1,說明主進程還未停止

(4.1)守護進程被join:

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os


# 守護進程p1執行
def daemon_func():
    time.sleep(1)
    print('daemon_func 進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


# 非守護進程p2執行
def non_daemon_func():
    time.sleep(2)
    print('non_daemon_func 進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


if __name__ == "__main__":
    print('主進程id {}'.format(os.getpid()))

    p1 = Process(target=daemon_func)
    p1.daemon = True
    p2 = Process(target=non_daemon_func)

    p1.start()
    p2.start()

    p1.join()  # join守護進程
主進程執行完後沒停止守護進程,並等待非守護進程執行完:
主進程id 24416
daemon_func 進程pid=27312,ppid=24416
non_daemon_func 進程pid=12408,ppid=24416
守護進程睡三秒,主進程仍會等待守護進程執行完:
主進程id 20336
non_daemon_func 進程pid=24528,ppid=20336
daemon_func 進程pid=16596,ppid=20336

  守護進程被join後,主進程會等待守護進程執行完。所以,join守護進程,還不如直接開非守護進程

(4.2)非守護進程被join

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os


# 守護進程p1執行
def daemon_func():
    time.sleep(1)
    print('daemon_func 進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


# 非守護進程p2執行
def non_daemon_func():
    time.sleep(2)
    print('non_daemon_func 進程pid={},ppid={}'.format(os.getpid(), os.getppid()))


if __name__ == "__main__":
    print('主進程id {}'.format(os.getpid()))

    p1 = Process(target=daemon_func)
    p1.daemon = True
    p2 = Process(target=non_daemon_func)

    p1.start()
    p2.start()

    p2.join()  # join非守護進程
非守護進程被join,主進程等待非守護進程,非守護進程停止後,當即停止守護進程。
------------------------------------------------------------------------------ 注意【1.主進程只join非守護進程】和【2.主進程沒有join任何進程】的區別:
1: 主進程會被join阻塞,等到非守護進程停止後,停止守護進程

2: 主進程執行完當即停止守護進程,若非守護進程未停止,等待

其實都是主進程的代碼執行完畢後,才停止子守護進程,只是1包含了手動join進程的代碼,阻塞後代碼纔算執行完畢。
-----------------------------------------------------------------
守護進程睡1秒後,非守護進程還在睡,主進程被阻塞住,所以守護進程有輸出
主進程id 11284
daemon_func 進程pid=5308,ppid=11284
non_daemon_func 進程pid=30364,ppid=11284
守護進程睡3秒後,非守護進程在2秒時睡醒後停止,主進程便在此時停止守護進程,所以守護進程沒有輸出
主進程id 24912
non_daemon_func 進程pid=19892,ppid=24912

 

 join在不少地方都有用到,預知join的行爲併合理利用join,避免產生死鎖

 

 terminate()

  停止進程,在Unix上是用SIGTERM信號完成的;在Windows上,使用TerminateProcess();

  注意,退出處理程序和finally子句等不會被執行

  被停止進程的子進程不會被停止

  避免使用此方法:使用該方法中止進程可能致使進程當前使用的任何共享資源被破壞或不可用於其它進程,最好只考慮該方法用在從不使用共享資源的進程上

 

 

參考:

官方文檔

 

若有意見或建議,一塊兒交流;若有侵權,請告知刪除。

相關文章
相關標籤/搜索