Где утечка памяти? Как отключать потоки во время многопроцессорной обработки в python?

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

В моем примере я использую пул из 50 joblib рабочих с бэкэндом threading.

Параллельный вызов (threading):

output = Parallel(n_jobs=50, backend  = 'threading')
    (delayed(get_output)(INPUT) 
        for INPUT in list)

Здесь Parallel зависает без ошибок, как только len(list) <= n_jobs, но только когда n_jobs => -1.

Чтобы обойти эту проблему, люди дают инструкции о том, как создать декоратор тайм-аута функции Parallel (get_output(INPUT)) в приведенном выше примере), используя multiprocessing:

Основная функция (оформлена):

@with_timeout(10)    # multiprocessing
def get_output(INPUT):     # threading
    output = do_stuff(INPUT)
    return output

Многообъемный декоратор:

def with_timeout(timeout):
    def decorator(decorated):
        @functools.wraps(decorated)
        def inner(*args, **kwargs):
            pool = multiprocessing.pool.ThreadPool(1)
            async_result = pool.apply_async(decorated, args, kwargs)
            try:
                return async_result.get(timeout)
            except multiprocessing.TimeoutError:
                return
        return inner
    return decorator

Добавление декоратора к другому рабочему коду приводит к утечке памяти после ~ 2x длины таймаута плюс крушение затмения.

Где эта утечка в декораторе?

Как отключить потоки во время многопроцессорной обработки в python?

Ответ 1

Невозможно убить Thread в Python без взлома.

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

Под капотом нить ThreadPool не заканчивается, а продолжает функционировать до конца.

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

Java-разработчики уже давно рассмотрели .

Если вы можете запускать свою функцию в отдельном процессе, тогда вы можете легко полагаться на логику тайм-аута, где сам процесс убивается после достижения таймаута.

Библиотека Pebble уже предлагает декораторы с таймаутом.