Python实现多进程(二)

       本文继续Python多任务编程思想(一)的话题,讨论python中进程的剩余几个概念:守护进程、重写Process类创建子进程以及进程池技术。

一. 守护进程

       通过上一小节的学习,我们已经知道主进程创建守护进程只需要将Process的daemon属性为true即可。虽然进程间是相互独立的,守护进程会在主进程"代码执行结束"时终止;③守护进程中不能再创建子进程。

        守护进程与守护线程存在很大差异,在学习完守护线程后,将对二者做出总结区分。

'''p1为守护进程,在主进程退出时,p1结束循环;p2为非守护进程,不会受到主进程的影响'''
import time, os, signal
from multiprocessing import Process

def foo():
    while True:
        print(time.ctime())
        time.sleep(3)

def bar():
    print("非守护进程启动")
    time.sleep(60)
    print("非守护进程结束")

p1=Process(target=foo)
p2=Process(target=bar)

#避免守护进程成为僵尸进程
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

if __name__ == '__main__':
    p1.daemon=True
    p1.start()
    p2.start()
    print("在主进程{}中创建了守护进程{}和非守护进程{}".format(os.getpid(), p1.pid, p2.pid))
    time.sleep(4)
    print("主进程{}结束!".format(os.getpid()))

运行结果:在打印主进程22828结束提示时,查看进程信息,可以看到守护进程22829已经退出,留下主进程22828和其子进程22830。提示非守护进程22830也运行结束时,再次查看进程,22828和22830均已退出。我们在上一小节提到mutiprocessing创建的子进程在执行结束后会返回主进程,所以我们查看进程信息时,主进程任然是存在的,只有守护进程22829因主进程代码执行完毕而退出。因此,我们的非守护进程并非孤儿进程,通过设置signal.signal(signal.SIGCHLD, signal.SIG_IGN),在主进程收到子进程退出的SIGCHLD信号时,做忽略处理,以交由系统处理,以此来避免僵尸的产生。

二. 创建自己的进程类

       除了使用Process类在实例化时传入target函数外,另一种常用的方式,还可以继承Process类,并重写run方法,run方法将在Process实例调用start方法后,自动运行。

from multiprocessing import Process
import time

class ClockProcess(Process):
    def __init__(self, value):
        # 调用父类的__init__方法
        super().__init__()
        self.value = value

    def run(self):
        '''重写run方法,子进程启动后,自动执行此方法'''
        for i in range(3):
            time.sleep(self.value)
            print(time.ctime())

p1 = ClockProcess(2)
p1.start()
p1.join()

三. 进程池技术

        多进程编程能并行执行多个任务,具有效率高,创建方便,运行独立,不受其他进程影响,数据安全等特点;但进程的创建和删除都需要消耗计算机的资源。如果有大量任务需要多进程完成,且可能需要频繁地创建和删除进程,将给计算机带来大量的资源消耗。为此,我们使用进程池,在进程池内运行一定数量的进程,通过这些进程完成进程池队列中的事件,直到事件执行完,减少进程的不断地创建删除过程。进程池的过程实现如下:

  1. 创建进程池,在进程池中放入适当进程
  2. 将事件加入到进程池队列
  3. 事件不断运行,直到所有事件运行完毕
  4. 关闭进程池,回收进程

上述过程用到的multiprocessing模块的类/方法总结如下:

multiprocessing 类/方法说明

from multiprocessing import Pool

pool = Pool(processes)

创建进程池对象

参数processes表示进程池中有多少个进程

pool.apply_async(func, args, kwds) 

将事件放入进程池队列

参数:func要执行的事件,args给func用元组传参,kwds 给func用字典传参

返回值:事件对象,可通过get()方法获取事件函数返回值

pool.map(func, iter)

将要完成的事件放入进程池

参数:func 要完成的事件函数,iter 可迭代对象给func传值

返回值:事件函数的返回值列表

pool.close()关闭进程池,不能再添加新的事件
pool.join()阻塞等待回收进程池

  3.1 apply_async 将事件放入进程池队列

'''方式一:使用"apply_async()"需要自己创建列表,维护返回值'''
import os
from multiprocessing import Pool
from time import sleep,ctime

def event(message):
    sleep(2)
    print("{} 子进程{} message={}".format(ctime(), os.getpid(), message))
    return '&' + str(message) + '&'

print(ctime(), '父进程PID:', os.getpid())
pool = Pool(4)
r_list = []

for i in range(10):
    element = pool.apply_async(event,(i,))
    r_list.append(element)

for e in r_list:
    print(ctime(), "事件函数返回值:", e.get())

pool.close()
print(ctime(), '父进程{}阻塞等待回进程池.'.format(os.getpid()))
pool.join()

运行结果:使用线程池完成event的10次调用总耗时只有6s,而如果是单线程最少需要10*2 s。进程池中的进程并行执行,一起返回,直到所有进程都执行完毕。

3.2 更简洁的方式map

'''方法二 使用进程池的map方法;返回值为列表,无需自己创建维护列表'''
import os
from multiprocessing import Pool
from time import sleep,ctime

def event(message):
    sleep(2)
    print("{} 子进程{} message={}".format(ctime(), os.getpid(), message))
    return '&' + str(message) + '&'

pool = Pool(4)

print(ctime(), '父进程PID:', os.getpid())
l = pool.map(event, range(10))
print(l)

pool.close()
print(ctime(), '父进程{}阻塞等待回进程池.'.format(os.getpid()))
pool.join()

运行结果:使用pool.map向进程池添加事件函数时,在父进程中获取返回值列表时是阻塞的,需要等待进程池返回所有事件函数的执行结果。但耗时上和apply_async是一致的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值