Python 多处理和垃圾收集
在py2.6+中,Python 多处理和垃圾收集,python,unix,garbage-collection,multiprocessing,Python,Unix,Garbage Collection,Multiprocessing,在py2.6+中,多处理模块提供了一个池类,因此可以执行以下操作: class Volatile(object): def do_stuff(self, ...): pool = multiprocessing.Pool() return pool.imap(...) 然而,在2.7.2的标准Python实现中,这种方法很快就会导致“IOError:[Errno 24]打开的文件太多”。显然,池对象从未被垃圾收集,因此其进程从未终止,积累了内部打开的任何
多处理
模块提供了一个池
类,因此可以执行以下操作:
class Volatile(object):
def do_stuff(self, ...):
pool = multiprocessing.Pool()
return pool.imap(...)
然而,在2.7.2的标准Python实现中,这种方法很快就会导致“IOError:[Errno 24]打开的文件太多”。显然,池
对象从未被垃圾收集,因此其进程从未终止,积累了内部打开的任何描述符。我认为这是因为以下几点起作用:
class Volatile(object):
def do_stuff(self, ...):
pool = multiprocessing.Pool()
result = pool.map(...)
pool.terminate()
return result
我想保留
imap的“惰性”迭代器方法;在这种情况下,垃圾收集器是如何工作的?如何修复代码?在python中,基本上无法保证什么时候东西会被破坏,在这种情况下,多处理池的设计目的不是这样的
正确的做法是跨多个函数调用共享单个池。最简单的方法是将池存储为类(或实例)变量:
class Dispatcher:
pool = multiprocessing.Pool()
def do_stuff(self, ...):
result = self.pool.map(...)
return result
最后,我传递了池
引用,并在池.imap
迭代器完成后手动终止它:
class Volatile(object):
def do_stuff(self, ...):
pool = multiprocessing.Pool()
return pool, pool.imap(...)
def call_stuff(self):
pool, results = self.do_stuff()
for result in results:
# lazy evaluation of the imap
pool.terminate()
如果将来有人偶然发现这个解决方案:chunksize参数在Pool.imap
中非常重要(与普通Pool.map
相反,后者并不重要)。我手动设置它,以便每个进程接收1+len(输入)/len(池)
作业。将其保留为默认值chunksize=1
可以提供与我完全不使用并行处理相同的性能。。。糟糕
我想使用orderedimap
与orderedmap
相比并没有什么真正的好处,我个人更喜欢迭代器。事实上,即使删除了对池
对象的所有用户引用,队列代码中没有任何任务,并且完成了所有垃圾收集,然后,进程仍然作为操作系统中不可用的僵尸,另外,我们有3个僵尸服务线程来自池
挂起(Python 2.7和3.4):
它终止其他服务线程,也终止子进程
我认为一个问题是,Python库中有一个资源泄漏bug,可以通过正确使用weakref
来修复
另一点是,池
的创建和终止非常昂贵(每个池包括3个服务线程,仅用于管理!),通常没有理由拥有比CPU核心多得多的工作进程(高CPU负载),或者根据另一个限制性资源拥有超过有限数量的工作进程(例如,网络带宽)。因此,合理的做法是将池更像一个单一的应用程序全局资源(可选地通过超时进行管理),而不是由闭包(或terminate()-因错误而采取的变通办法)持有的快速对象
例如:
try:
_unused = pool # reload safe global var
except NameError:
pool = None
def get_pool():
global pool
if pool is None:
atexit.register(stop_pool)
pool = Pool(CPUCORES)
return pool
def stop_pool():
global pool
if pool:
pool.terminate()
pool = None
Pool()
是否在内部分叉?您的解决方案如何“更新”派生进程的状态,使其在实际调用do\u stuff()
时生效?(与计算Dispatcher
时相反)手动将所有内容与主进程保持同步听起来相当复杂。将池存储为成员变量很好;我不理解您的状态问题--您希望共享什么状态?如果您希望您的进程共享相同的解释器状态,那么您可能应该改为使用线程…谢谢@Autopulated.threads由于GIL的原因,不要做太多。我想共享的状态是调用了do_stuff
的对象(=对大型只读对象进行昂贵的操作,无法复制)。您能否给出一个提示,说明…
在您的池.map(…)
?当然。…
是只读的,但对Volatile
对象的成员变量执行CPU繁重的操作。我希望这些操作并行执行,以提高性能。对象在dou stuff
期间不会发生变化。不,我的意思是我喜欢迭代器。顺便说一句,每个生成器都是迭代器。在我的情况下,我必须使用c所有pool.terminate()
以在该工作之后获取gc.collect()
。否则,python不会gc池中引用的那些对象,即使使用显式del pool
。
>>> ths = threading.enumerate()
>>> for th in ths:
... try: th.name, th._state, th._Thread__target
... except AttributeError: pass
...
('MainThread', 1, None)
('Thread-8', 0, <function _handle_tasks at 0x01462A30>)
('Thread-9', 0, <function _handle_results at 0x014629F0>)
('Thread-7', 0, <function _handle_workers at 0x01462A70>)
>>> ths[-1]._state = multiprocessing.pool.CLOSE # or TERMINATE
>>> threading.enumerate()
[<_MainThread(MainThread, started 5632)>]
>>>
try:
_unused = pool # reload safe global var
except NameError:
pool = None
def get_pool():
global pool
if pool is None:
atexit.register(stop_pool)
pool = Pool(CPUCORES)
return pool
def stop_pool():
global pool
if pool:
pool.terminate()
pool = None