python:多进程池嵌套以及内存管理

前言

在使用进程嵌套进行并行调参的过程中,使用了close关闭进程池,使得创建的进程池中的进程变为stopped状态,造成了内存泄漏,最后导致了内存溢出,最后发现了问题整理了这篇文章。

1.进程池

当创建的子进程数量不多时,可以使用multiprocessing.Process动态创建。但是,当创建的子进程数量很多时,因为创建子进程也需要开销,如果按照上述方面创建,代价较高,因此使用进程池进行创建子进程。另一方面,使用进程池能够最大限度的利用资源。一个CPU能并行的进程数是有限制的,超过这个数量时,进程之间需要进行切换,反而会降低效率。

python中使用multiprocessing.Pool创建进程池

import multiprocessing as mp

def func(i):
	print(i)

if __name__ == "__main__":
	num_cores = 4
	pool = mp.Pool(num_cores)
	for i in range(10):
		pool.map(func, (i, ))
		
	pool.close()
	print("end")

输出:

2
6
0
3
7
1
5
9
4
8
end

map函数,使进程阻塞执行,直到子进程全部结束,才会继续执行主进程。

2. 多进程池嵌套

由于python的GIL(Global Interpreter Lock,即全局解释器锁)存在(参考 python:GIL介绍),对于CPU频繁型的任务,线程并行并不能提高效率。因此,对于进程中如果存在仍需并行的任务时,仍需使用进程并行去解决,这涉及到如何解决进程嵌套问题。

multiprocessing.Pool创建的进程池中的子进程,默认为守护进程(即主进程结束则依赖主进程的子进程也会结束),无法在进程中创建子进程,因此需要修改进程池中子进程的属性。

重构multiprocessing.Pool类

# 重构multiprocessing.Process类,将进程始终定义为非守护进程
class NoDaemonProcess(multiprocessing.Process):
    # make 'daemon' attribute always return False
    def _get_daemon(self):
        return False
    def _set_daemon(self, value):
        pass
    daemon = property(_get_daemon, _set_daemon)
	
# 重构multiprocessing.Pool类
class Pool(multiprocessing.Pool):
    Process = NoDaemonProcess

3. 内存管理

  • 内存溢出和内存泄漏
    内存溢出, 指系统已经无法再分配所需的空间。
    内存泄漏, 指没有回收已经使用过的资源,导致内存没有被释放。当内存泄漏的次数过多时,可能导致内存溢出。

  • pool.close 与 pool.terminate的区别
    close和terminate都能够阻止进程池接受新的请求,但是terminate会中断正在执行的子进程操作,而close不会。terminate会直接注销进程池中创建的子进程,将内存回收,而close则会在主进程正常退出时才会回收资源,若此时发生意外中断了主进程,则close后的进程池中的进程不会被注销,而是会变成stopped状态或者zombie状态,占用的内存资源也无法释放,造成内存泄漏。

你可能感兴趣的:(Python)