python多进程——multiprocessing.Process

时间:2019-09-16
本文章向大家介绍python多进程——multiprocessing.Process,主要包括python多进程——multiprocessing.Process使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

  简介                               

  multiprocessing是一个使用类似于threading模块的API支持生成进程的包。该multiprocessing软件包提供本地和远程并发。因此,该multiprocessing模块允许程序员充分利用给定机器上的多个处理器。可以在Unix和Windows上运行。

multipleprocessing文档

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()

结果:

我是主进程, 进程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()不阻塞,直接执行
'''

结果:

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子句等不会被执行

  被中止进程的子进程不会被中止

  避免使用此方法:使用该方法停止进程可能导致进程当前使用的任何共享资源被破坏或不可用于其它进程,最好只考虑该方法用在从不使用共享资源的进程上

参考:

官方文档

如有意见或建议,一起交流;如有侵权,请告知删除。

原文地址:https://www.cnblogs.com/Magic-Dev/p/11420692.html