最近在部署模型的时候发现需要多进程功能充分利用机器性能,提高推理的效率。因此研究了一下python的多进程操作,发现只需要短短的几行代码,非常方便,在此记录一下。
利用processing包的进程池可以非常方便地构建,下面便是processing包使用的基础信息:
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。多进程是实现并发的手段之一,需要注意的问题是:
1、很明显需要并发执行的任务通常要远大于核数
2、一个操作系统不可能无限开启进程,通常有几个核就开几个进程
3、进程开启过多,效率反而会下降(开启进程是需要占用资源的,而且开启多余核数目的进程也无法做到并行)
例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态生成多个进程,十几个还好,但如果是上百个上千个,手动的去限制进程数量却又过于繁琐,此时可以发挥进程池的功效。
我们就可以通过一个进程池来控制进程数目,比如http的进程模式,规定最小进程数和最大进程数。。。
ps:对于远程过程调用的高级应用程序而言,比如使用进程池,Pool可以提供指定数目的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。
创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后至始至终使用这三个进程去执行所有任务,不会开启其他进程
Pool([numprocess [,initializer [, initargs]]]):创建进程池
参数介绍
numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值 initializer:是每个工作进程启动时要执行的可调用对象,默认为None initargs:是要传给initializer的参数组
方法介绍:
p.apply(func[, args[, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。需要强调的是,此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async() p.apply_async(func [func [, args [, kwargs]]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。此方法的结果是AsynResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。 p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,他们将在工作进程终止前完成 p.join():等待所有工作进程退出,此放大只能在close()或reminate()之后调用
代码示例:
def mission(param):
'''
任务
:param param:
:return:
'''
id, content = param
t_start = time.time()
log.info("开始执行子进程:{}".format(id))
update_sql = ("update video set status='DOING' where id={}".format(id))
db.execute(update_sql, commit=True)
#执行进程任务
try:
# val = os.system("./sh")
print('文本内容:{}'.format(content))
time.sleep(random.random()*2)
t_stop = time.time()
log.info("进程{}执行完成, 执行用时{}".format(param, t_stop-t_start))
#更新数据库任务状态,已完成状态
update_sql = ("update video set status='DONE' where id={}".format(id))
db.execute(update_sql, commit=True)
except Exception as e:
log.error('error:{}'.format(e))
update_sql = ("update video set status='FAILED' where id={}".format(id))
db.execute(update_sql, commit=True)
log = Logger('../test.log').logger
# sql_log = Logger('../sql.log').logger
def main(items, pool):
'''
启动进程
:return:
'''
res = []
if not items:
log.info("未查询到任务,休眠2s")
time.sleep(2)
return res
for i in items:
res.append(pool.apply_async(func=mission, args=(i,), callback=cb()))
return res
我的代码功能中misson就是每一个进程需要执行的任务,然后在main函数中将所有等待执行的任务加入到进程池中,开始执行。
需要注意的点:
p.join()是等待本次所有任务执行完成后退出进程,后续如果启动进程需重新启动,如果需要持续添加任务的时候不用加。
在调试的时候进程的日志需要打印出来,不然很难发现哪个步骤出了问题。