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

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

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

 

 

参考:

官方文档

 

若有意见或建议,一块儿交流;若有侵权,请告知删除。

相关文章
相关标签/搜索