Пул процессов 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 ответа
Вы читали руководство по программированию? Там есть много вещей о глобальных переменных. Есть еще больше ограничений под Windows. Вы не говорите, на какой платформе вы работаете, но это может быть проблемой, если вы работаете под Windows. Из приведенной выше ссылки
Глобальные переменные
Имейте в виду, что если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если он есть), может не совпадать со значением в родительском процессе в момент вызова Process.start(),
Однако глобальные переменные, которые являются только константами уровня модуля, не вызывают проблем.
Process
(через пул или иным образом) не будет иметь __name__
из '__main__'
поэтому он не выполнит ничего, что зависит от этого условия, включая exec
заявления, от которых вы зависите, чтобы найти свой meth
, конечно.
Почему ты так хочешь иметь это exec
защищен условием, которое по замыслу IS будет ложным в вашем подпроцессе, но этот подпроцесс будет зависеть (противоречиво!) от выполнения этого exec
...?! Это действительно ошеломляет мой разум...
Как я прокомментировал выше, все ваши примеры работают так, как вы ожидаете, на моем компьютере с Linux (Debian Lenny, Python2.5, обработка 0.52, см. Тестовый код ниже).
Кажется, существует множество ограничений на окна для объектов, которые вы можете передавать из одного процесса в другой. Читая документ, на который указал Ник, кажется, что в окне отсутствующего форка он запустит совершенно новые модули импорта интерпретатора 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)