Python 进程池与作用域
我正在尝试在一个循环中运行自动生成的代码(这个代码可能会一直运行下去),这是为了进行遗传编程。我想使用多进程池来实现这个,因为我不想每次都创建新进程带来的性能损耗,而且如果运行时间太长,我可以终止这个进程(而线程就做不到这一点)。
基本上,我的程序是
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 个回答
正如我之前评论的,你的所有例子在我的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)
通过进程池或其他方式创建的Process
,它的__name__
不会是'__main__'
,所以它不会执行任何依赖于这个条件的东西——包括你用来找到meth
的exec
语句。
你为什么那么想要让这个exec
被一个条件保护,而这个条件在你的子进程中设计上就会是假的呢?而且这个子进程又依赖(这不是自相矛盾吗!)于这个exec
的执行...?!这真让我感到困惑...
你有没有看过这个编程指南? 里面有很多关于全局变量的内容。在Windows系统下还有更多的限制。你没有说明你使用的是哪个平台,但如果你是在Windows上运行,这可能就是问题所在。根据上面的链接:
全局变量
要记住,如果在子进程中运行的代码尝试访问一个全局变量,那么它看到的值(如果有的话)可能和在调用Process.start()时父进程中的值不一样。
不过,作为模块级常量的全局变量是没有问题的。