Python 多处理和垃圾收集

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]打开的文件太多”。显然,池对象从未被垃圾收集,因此其进程从未终止,积累了内部打开的任何

在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()
        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
可以提供与我完全不使用并行处理相同的性能。。。糟糕


我想使用ordered
imap
与ordered
map
相比并没有什么真正的好处,我个人更喜欢迭代器。

事实上,即使删除了对
对象的所有用户引用,队列代码中没有任何任务,并且完成了所有垃圾收集,然后,进程仍然作为操作系统中不可用的僵尸另外,我们有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