Я пишу пакет Python, который считывает список модулей (вместе со вспомогательными данными) из файла конфигурации.

Затем я хочу перебрать каждый из динамически загружаемых модулей и вызвать в нем функцию do_work (), которая порождает новый процесс, чтобы код выполнялся АСИНХРОННО в отдельном процессе.

На данный момент я импортирую список всех известных модулей в начале моего основного скрипта - это неприятный хакер, который я считаю, и он не очень гибкий, а также затрудняет обслуживание.

Это функция, запускающая процессы. Я хотел бы изменить его, чтобы динамически загружать модуль при его обнаружении. Ключ в словаре - это имя модуля, содержащего код:

def do_work(work_info):
  for (worker, dataset) in work_info.items():
    #import the module defined by variable worker here...

    # [Edit] NOT using threads anymore, want to spawn processes asynchronously here...

    #t = threading.Thread(target=worker.do_work, args=[dataset])
    # I'll NOT dameonize since spawned children need to clean up on shutdown
    # Since the threads will be holding resources
    #t.daemon = True
    #t.start()

Вопрос 1

Когда я вызываю функцию в своем скрипте (как написано выше), я получаю следующую ошибку:

AttributeError: объект 'str' не имеет атрибута 'do_work'

Это имеет смысл, поскольку ключ словаря представляет собой строку (имя импортируемого модуля).

Когда я добавляю заявление:

работник импорта

Перед созданием потока я получаю сообщение об ошибке:

ImportError: нет модуля с именем worker

Это странно, поскольку используется имя переменной, а не значение, которое она содержит - когда я печатаю переменную, я получаю значение (как я ожидал), что происходит?

Вопрос 2

Как я уже упоминал в разделе комментариев, я понимаю, что функция do_work (), написанная в порожденных дочерних элементах, должна очищаться после себя. Я понимаю, что нужно написать функцию clean_up, которая вызывается, когда do_work () успешно завершилась или было обнаружено необработанное исключение - есть ли что-нибудь еще, что мне нужно сделать, чтобы гарантировать, что ресурсы не просачиваются или не оставляют ОС в нестабильном состоянии?

Вопрос 3

Если я закомментирую оператор флага t.daemon, будет ли код работать АСИНХРОННО ?. Работа, выполняемая порожденными детьми, довольно интенсивна, и я не хочу ждать, пока один ребенок закончит, прежде чем порождать другого ребенка. Кстати, я знаю, что потоки в Python на самом деле являются своего рода разделением времени / нарезкой - это нормально

Наконец, есть ли лучший (более питонический) способ делать то, что я пытаюсь сделать?

[Изменить]

Прочитав немного больше о Pythons GIL и потоковой передаче (кхм - хак) в Python, я считаю, что лучше использовать вместо этого отдельные процессы (по крайней мере, IIUC, сценарий может использовать преимущества нескольких процессов, если они доступны), поэтому я порождать новые процессы вместо потоков.

У меня есть пример кода для создания процессов, но он немного тривиален (с использованием лямбад-функций). Я хотел бы знать, как его расширить, чтобы он мог иметь дело с запущенными функциями в загруженном модуле (как я делаю выше).

Это отрывок из того, что у меня есть:

def do_mp_bench():
    q = mp.Queue() # Not only thread safe, but "process safe"
    p1 = mp.Process(target=lambda: q.put(sum(range(10000000))))
    p2 = mp.Process(target=lambda: q.put(sum(range(10000000)))) 
    p1.start()
    p2.start()
    r1 = q.get()
    r2 = q.get()
    return r1 + r2

Как я могу изменить это, чтобы обработать словарь модулей и запустить функцию do_work () в каждом загруженном модуле в новом процессе?

0
morpheous 31 Май 2010 в 11:33

2 ответа

Лучший ответ

Это было изменено для использования документации import () здесь: import и переработан для использования запрошенного модуля многопроцессорности, как описано здесь: multiprocessing. Это не было проверено.

def do_work(work_info):
    q = mp.Queue()
    for (worker, dataset) in work_info.items():
      xworker = __import__(worker)
      p = mp.Process(target=xworker.do_work, args=dataset).start()
      q.put(p)
    while not q.empty():
      r = q.get()
1
Gabriel 31 Май 2010 в 09:47

Вопрос 1: используйте __import__().

Вопрос 2: почему бы просто не выполнить очистку в конце функции do_work()?

Вопрос 3: поток демона IIRC просто означает, что программа не будет автоматически ждать завершения этого потока.

2
Bastien Léonard 31 Май 2010 в 08:03