Распределение силы процесса в многопроцессорном пуле Python

Этот вопрос возникает в результате попытки объединить ведение журнала с многопроцессорным пулом. Под Linux делать нечего; модуль, содержащий мой рабочий метод пула, наследует основные свойства регистратора приложений. Под Windows мне нужно инициализировать регистратор в каждом процессе, что я делаю, запустив pool.map_async с методом инициализатора. Проблема в том, что метод работает так быстро, что в одних процессах он выполняется более одного раза, а в других - нет. Я могу заставить его работать правильно, если добавлю к методу небольшую задержку, но это кажется неэлегантным.

Есть ли способ заставить пул распределять процессы равномерно?

(немного предыстории: http://plumberjack.blogspot.de/2010/09/using-logging-with-multiprocessing.html)

Код следующий, я не могу опубликовать весь модуль ;-) Вызов такой:

# Set up logger on Windows platforms
if os.name == 'nt':
    _ = pool.map_async(ml.worker_configurer,
                       [self._q for _ in range(mp.cpu_count())])

Функция ml.worker_configurer такова:

def worker_configurer(queue, delay=True):
    h = QueueHandler(queue)
    root = logging.getLogger()
    root.addHandler(h)
    root.setLevel(logging.DEBUG)
    if delay:
        import time
        time.sleep(1.0)
    return

Конфигуратор нового воркера

def worker_configurer2(queue):
    root = logging.getLogger()
    if not root.handlers:
        h = QueueHandler(queue)
        root.addHandler(h)
        root.setLevel(logging.DEBUG)
    return

person Paul Wells    schedule 16.11.2014    source источник
comment
Как выглядит ваш код?   -  person Joel Cornett    schedule 16.11.2014
comment
Добавил к моему вопросу.   -  person Paul Wells    schedule 16.11.2014
comment
Pool действительно полезен для быстрого распараллеливания, но не очень хорош, если вам нужен точный контроль над вашими процессами.   -  person Joel Cornett    schedule 16.11.2014
comment
Я использую пул для выступления. Я могу повторно использовать его на нескольких этапах вычислений без дополнительных затрат на запуск процессов, особенно в Windows.   -  person Paul Wells    schedule 16.11.2014
comment
Я понимаю. Лучше всего переместить конфигурацию регистратора внутри каждой вашей рабочей функции. Например, определите функцию get_logger, которая сначала проверяет наличие регистратора и при необходимости настраивает его перед возвратом настроенного объекта регистратора. Этот регистратор может быть глобальным, поскольку процессы имеют отдельные среды.   -  person Joel Cornett    schedule 17.11.2014
comment
Первоначально у меня была конфигурация регистратора в качестве первой части моей рабочей функции, но поскольку функция вызывалась несколько раз для каждого рабочего, я получил еще больше обработчиков регистратора и дублирующих, трехкратных ... и т. Д. Сообщений! Я предполагаю, что уловка состоит в том, чтобы сначала проверить, настроен ли регистратор, который мне теперь придется гуглить ...   -  person Paul Wells    schedule 17.11.2014
comment
Спасибо, @JoelCornett. Это помогло. Я добавил свой новый worker_configurer к вопросу   -  person Paul Wells    schedule 17.11.2014
comment
Позвольте нам продолжить это обсуждение в чате.   -  person Paul Wells    schedule 17.11.2014


Ответы (1)


Вы можете сделать что-то вроде этого:

sub_logger = None

def get_logger():
    global sub_logger
    if sub_logger is None:
        # configure logger

    return sub_logger

def worker1():
    logger = get_logger()
    # DO WORK

def worker2():
    logger = get_logger()
    # DO WORK

pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
result = pool.map_async(worker1, some_data)
result.get()
result = pool.map_async(worker2, some_data)
result.get()
# and so on and so forth

Поскольку каждый процесс имеет собственное пространство памяти (и, следовательно, собственный набор глобальных переменных), вы можете установить начальное глобальное средство ведения журнала на None и настроить средство ведения журнала только в том случае, если оно не было ранее настроено.

person Joel Cornett    schedule 17.11.2014
comment
Хм, я вижу, как это работает, но я уже реализовал конфигуратор, который проверяет существующий регистратор на наличие обработчиков, поскольку проблема возникает из-за дублирования обработчика. Вы видите недостатки в моем решении? Лучше ли глобальный подход? - person Paul Wells; 17.11.2014