Python 进程池与作用域

0 投票
3 回答
4032 浏览
提问于 2025-04-15 14:38

我正在尝试在一个循环中运行自动生成的代码(这个代码可能会一直运行下去),这是为了进行遗传编程。我想使用多进程池来实现这个,因为我不想每次都创建新进程带来的性能损耗,而且如果运行时间太长,我可以终止这个进程(而线程就做不到这一点)。

基本上,我的程序是

if __name__ == '__main__':    
    pool = Pool(processes=1)            
    while ...:
        source = generate() #autogenerate code
        exec(source)
        print meth() # just a test, prints a result, since meth was defined in source
        result = pool.apply_async(meth)
        try:
            print result.get(timeout=3)  
        except:
           pool.terminate()

这是应该能正常工作的代码,但实际上并没有,我得到了

AttributeError: 'module' object has no attribute 'meth'

看起来多进程池只识别在最顶层定义的方法。如果想让它运行动态创建的方法,有什么建议吗?

编辑:问题在使用进程时也是一样的,也就是说

source = generated()
exec(source)
if __name__ == '__main__':    
    p = Process(target = meth)
    p.start()

可以正常工作,而

if __name__ == '__main__':    
    source = generated()
    exec(source)
    p = Process(target = meth)
    p.start()

则不行,并且出现了属性错误(AttributeError)。

3 个回答

0

正如我之前评论的,你的所有例子在我的Linux系统上(Debian Lenny,Python2.5,processing 0.52)都能正常工作,下面是测试代码。

在Windows上,从一个进程传输到另一个进程的对象似乎有很多限制。根据Nick提到的文档,在Windows上,由于缺少fork功能,它会运行一个全新的Python解释器,导入模块并对应该传递的对象进行序列化和反序列化。如果这些对象无法被序列化,我想你会遇到类似你之前遇到的问题。

因此,一个完整(虽然不)工作的例子可能对诊断问题有帮助。答案可能就在你隐藏的那些看似不相关的内容中。

from processing import Pool
import os

def generated():
    return (
"""
def meth():
    import time
    starttime = time.time()
    pid = os.getpid()
    while 1:
        if time.time() - starttime > 1:
            print "process %s" % pid
            starttime = starttime + 1

""")


if __name__ == '__main__':
    pid = os.getpid()
    print "main pid=%s" % pid
    for n in range(5):
        source = generated() #autogenerate code
        exec(source)
        pool = Pool(processes=1)            
        result = pool.apply_async(meth)
        try:
            print result.get(timeout=3)  
        except:
           pool.terminate()

另一个建议是使用线程。是的,你可以,即使你不知道你生成的代码是否会停止,或者你的生成代码是否有不同嵌套的循环。循环根本没有限制,这正是使用生成器的一个关键点(提取控制流)。我看不出这为什么不能应用到你正在做的事情上。[同意这可能比独立进程更复杂] 下面是例子。

import time

class P(object):
    def __init__(self, name):
        self.name = name
        self.starttime = time.time()
        self.lastexecutiontime = self.starttime
        self.gen = self.run()

    def toolong(self):
        if time.time() - self.starttime > 10:
            print "process %s too long" % self.name
            return True
        return False

class P1(P):
    def run(self):
        for x in xrange(1000):
            for y in xrange(1000):
                for z in xrange(1000):
                    if time.time() - self.lastexecutiontime > 1:
                        print "process %s" % self.name
                        self.lastexecutiontime = self.lastexecutiontime + 1
                        yield
        self.result = self.name.uppercase()

class P2(P):
    def run(self):
        for x in range(10000000):
            if time.time() - self.lastexecutiontime > 1:
                print "process %s" % self.name
                self.lastexecutiontime = self.lastexecutiontime + 1
                yield
        self.result = self.name.capitalize()

pool = [P1('one'), P1('two'), P2('three')]
while len(pool) > 0:
    current = pool.pop()
    try:
        current.gen.next()
    except StopIteration:
        print "Thread %s ended. Result '%s'" % (current.name, current.result) 
    else:
        if current.toolong():
            print "Forced end for thread %s" % current.name 
        else:
            pool.insert(0, current)
2

通过进程池或其他方式创建的Process,它的__name__不会是'__main__',所以它不会执行任何依赖于这个条件的东西——包括你用来找到methexec语句。

你为什么那么想要让这个exec被一个条件保护,而这个条件在你的子进程中设计上就会是假的呢?而且这个子进程又依赖(这不是自相矛盾吗!)于这个exec的执行...?!这真让我感到困惑...

3

你有没有看过这个编程指南? 里面有很多关于全局变量的内容。在Windows系统下还有更多的限制。你没有说明你使用的是哪个平台,但如果你是在Windows上运行,这可能就是问题所在。根据上面的链接:

全局变量

要记住,如果在子进程中运行的代码尝试访问一个全局变量,那么它看到的值(如果有的话)可能和在调用Process.start()时父进程中的值不一样。

不过,作为模块级常量的全局变量是没有问题的。

撰写回答