python多进程


Python中的multiprocessing模块支持多进程编程,主要功能有支持子进程、进程间通信、同步等,涉及到的类有Process,Queue,Pipe,Lock等。

Process类介绍

定义:

Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

参数:

group:参数未使用,默认为None

target:表示生成的子进程要执行的程序

name:表示子进程的名称

args:表示子进程执行程序的参数元组

kwargs:表示子进程执行程序的参数字典

方法:

方法/属性 说明
start() 启动进程,调用进程中的run()方法。
run() 进程启动时运行的方法,正是它去调用target指定的函数,如果要自己实现子进程类,一定要重写run方法
terminate() 强制终止进程,不会进行任何清理操作。如果该进程终止前,创建了子进程,那么该子进程在其强制结束后变为僵尸进程;如果该进程还保存了一个锁那么也将不会被释放,进而导致死锁。使用时,要注意。
is_alive() 判断某进程是否存活,存活返回True,否则False。
join([timeout]) 主线程等待子线程终止。timeout为可选择超时时间;需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 。
daemon 默认值为False,如果设置为True,代表该进程为后台守护进程;当该进程的父进程终止时,该进程也随之终止;并且设置为True后,该进程不能创建子进程,设置该属性必须在start()之前
pid 进程id
name 进程名称
exitcode 进程运行时为None,如果为-N,表示被信号N结束了。
authkey 进程身份验证,默认是由os.urandom()随机生成32字符的字符串。这个键的用途是设计涉及网络连接的底层进程间的通信提供安全性,这类连接只有在具有相同身份验证才能成功。

Process类使用

创建子进程

有两种方法创建子进程,一种是直接使用Process类,另一种是通过继承自己写子进程类。

方法一

import time
import random
from multiprocessing import Process

def task(name):
    """
    子进程需要调用的程序
    :param name: 
    :return: 
    """
    print('%s task begin' % name)
    time.sleep(random.randrange(1, 5))
    print('%s task end' % name)

if __name__ == '__main__':
    #实例化四个子进程对象,主要args括号内参数末尾加逗号
    p1 = Process(target=task, args=('张三',)) 
    p2 = Process(target=task, args=('李四',))
    p3 = Process(target=task, args=('王五',))
    p4 = Process(target=task, args=('赵六',))

    #调用start方法,开启四个进程,实质是调用run方法
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    print('主进程运行完毕')

运行结果:

主进程运行完毕
张三 task begin
李四 task begin
王五 task begin
赵六 task begin
李四 task end
王五 task end
张三 task end
赵六 task end

可以看到子进程的开始在主进程的代码完成之后,主要是因为操作系统生成子进程时要先选定子进程的地址空间,然后将父进程的地址空间的内容拷贝到子进程的地址空间,开销比较大,比较费时。

方法二:

import time
import random
from multiprocessing import Process

class Task(Process):
    """Task子线程,继承Process类"""
    def __init__(self, name):
        super().__init__()
        self.name = name

    #必须要重写run方法
    def run(self):
        print('%s task begin' % self.name)
        time.sleep(random.randrange(1, 5))
        print('%s task end' % self.name)


if __name__ == '__main__':
    #实例化四个子进程对象
    p1 = Task('张三') 
    p2 = Task('李四')
    p3 = Task('王五')
    p4 = Task('赵六')

    #调用start方法,开启四个进程,实质是调用run方法
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    print('主进程运行完毕')

运行结果:

主进程运行完毕
张三 task begin
李四 task begin
王五 task begin
赵六 task begin
李四 task end
王五 task end
赵六 task end
张三 task end

获得进程id

import time, os
from multiprocessing import Process

def task():
    print('%s begin, parent id is %s' % (os.getpid(), os.getppid()))
    print('%s is running' % os.getpid())
    print('%s is done, parent id is %s' % (os.getpid(), os.getppid()))

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    print("主进程运行完毕,进程id为 %s, parent id is %s" % (os.getpid(), os.getppid()))

运行结果:

主进程运行完毕,进程id为 38980, parent id is 37264
46136 begin, parent id is 38980
46136 is running
46136 is done, parent id is 38980

join方法

前面的例子中子进程都是在主进程运行完毕后才开始运行,如果父进程和子进程的任务互相独立,没有问题,各自运行就可以,但如果父进程需要子进程的运行结果才能继续运行下去,这种情况就无法满足,此时就需要join方法。

join方法可以使主进程阻塞,直到子进程运行完毕,主进程才继续运行。

对于上面获取进程pid的例子,只需要增加一行代码,就可以使子进程先运行,主进程阻塞等待。

import time, os
from multiprocessing import Process

def task():
    print('%s begin, parent id is %s' % (os.getpid(), os.getppid()))
    print('%s is running' % os.getpid())
    print('%s is done, parent id is %s' % (os.getpid(), os.getppid()))

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()

    print("主进程运行完毕,进程id为 %s, parent id is %s" % (os.getpid(), os.getppid()))

运行结果:

32816 begin, parent id is 42904
32816 is running
32816 is done, parent id is 42904
主进程运行完毕,进程id为 42904, parent id is 37264

join实现并发

如果多个子进程调用join方法,子进程之间还是并发运行的,只是主进程要等待所有的子进程运行完毕后才能继续运行

import time
import random
from multiprocessing import Process

def task(name):
    """
    子进程需要调用的程序
    :param name: 
    :return: 
    """
    print('%s task begin' % name)
    time.sleep(random.randrange(1, 5))
    print('%s task end' % name)

if __name__ == '__main__':
    #实例化四个子进程对象,主要args括号内参数末尾加逗号
    p1 = Process(target=task, args=('张三',)) 
    p2 = Process(target=task, args=('李四',))
    p3 = Process(target=task, args=('王五',))
    p4 = Process(target=task, args=('赵六',))

    #调用start方法,开启四个进程,实质是调用run方法
    p1.start()
    p2.start()
    p3.start()
    p4.start()

    p1.join()
    p2.join()
    p3.join()
    p4.join()
    print('主进程运行完毕')

运行结果为:

张三 task begin
王五 task begin
李四 task begin
赵六 task begin
李四 task end
王五 task end
赵六 task end
张三 task end
主进程运行完毕

那如果子进程之间的运行结果也有依赖怎么办?比如子进程2要等待子进程1的运行结果才能继续运行,此时也可以用join方法来实现

join方法实现串行

import time
import random
from multiprocessing import Process


def task(name):
    """
    子进程需要调用的程序
    :param name:
    :return:
    """
    print('%s task begin' % name)
    time.sleep(random.randrange(1, 5))
    print('%s task end' % name)


if __name__ == '__main__':
    # 实例化四个子进程对象,主要args括号内参数末尾加逗号
    p1 = Process(target=task, args=('张三',))
    p2 = Process(target=task, args=('李四',))
    p3 = Process(target=task, args=('王五',))
    p4 = Process(target=task, args=('赵六',))

    pl = (p1, p2, p3, p4)
    for p in pl:
        p.start()
        p.join()

    print('主进程运行完毕')

运行结果:

张三 task begin
张三 task end
李四 task begin
李四 task end
王五 task begin
王五 task end
赵六 task begin
赵六 task end
主进程运行完毕

由结果可以看到,四个子进程时按顺序进行执行的,主进程待所有子进程结束后才开始执行后续代码。如果此时用is_alive方法查看子进程的状态,会发现返回值都是false,说明子进程已在主进程结束前结束,形成僵尸进程。

守护进程

主进程可以将自己开启的子进程设置为守护进程,主进程运行结束后,如果子进程是守护进程,那么不管子进程是否运行完毕,都会随主进程一起结束。守护进程无法再去生成子进程。

注意:设置守护进程后,一定要加上join方法,不然子进程还没运行就随主进程结束了

import time
import random
from multiprocessing import Process

def task(name):
    """
    子进程需要调用的程序
    :param name:
    :return:
    """
    print('%s task begin' % name)
    time.sleep(random.randrange(1, 5))
    print('%s task end' % name)

if __name__ == '__main__':
    #实例化四个子进程对象,主要args括号内参数末尾加逗号
    p1 = Process(target=task, args=('张三',), daemon=True)

    p1.start()
    p1.join()

    print('主进程运行完毕')
    print(p1.is_alive())

运行结果为:

张三 task begin
张三 task end
主进程运行完毕
False

#如果不加join方法,运行结果为:
#主进程运行完毕
#True

还有一点要注意,子进程和主进程的地址空间是不一样的,改变其中的一个,另一个并不会改变


文章作者: likai
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 likai !
评论
  目录