Тайм-аут для каждого потока в ThreadPool в Python

4

Я использую Python 2.7.

В настоящее время я использую ThreadPoolExecuter следующим образом:

params = [1,2,3,4,5,6,7,8,9,10]
with concurrent.futures.ThreadPoolExecutor(5) as executor:
    result = list(executor.map(f, params))

Проблема в том, что f иногда работает слишком долго. Всякий раз, когда я запускаю f, я хочу ограничить его запуск до 100 секунд, а затем убить его.

В конце концов, для каждого элемент x в param, я хотел бы иметь представление о том или не f должен был быть убит, и в случае, если это не было - то, что было возвращаемое значение. Даже если f отключается для одного параметра, я все еще хочу запустить его со следующими параметрами.

У метода executer.map есть параметр timeout, но он устанавливает тайм-аут для всего прогона с момента вызова executer.map, а не для каждого потока отдельно.

Каков самый простой способ получить мое желаемое поведение?

  • 1
    В Python нет прямого способа уничтожить поток. Если timeout вы передаете map истекает, он на самом деле не прерывает потоки Executor, он просто future.result(timeout) вызывающий внутреннее TimeoutError исключения TimeoutError . Рабочие потоки будут продолжать работать в фоновом режиме. Если вам нужно, чтобы поток был фактически завершен, вам нужна рабочая функция, чтобы проверить наличие какого-либо флага, который родитель может установить после истечения времени ожидания. Однако это может быть нелегко реализовать, в зависимости от того, что выполняет рабочая функция.
  • 0
    @ Дано: Понятно. Процесс, продолжающийся в фоновом режиме, - это то, с чем я могу жить. Но допустим, что поток обработки параметров params [4] застрял, могу ли я все же получить результат процессов обработки параметров params [5] в params [9]?
Показать ещё 1 комментарий
Теги:
multithreading
python-2.7
future
concurrent.futures

1 ответ

4

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

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

Один из вариантов - обернуть f в другую функцию, которая будет выходить на не более 100 секунд:

from multiprocessing import Pool

def timeout_f(arg):
    pool = Pool(processes=1)
    return pool.apply_async(f, [arg]).get(timeout=100)

Затем ваш код изменится на:

    result = list(executor.map(timeout_f, params))

Кроме того, вы можете написать свой собственный поток/процесс управления:

from multiprocessing import Process
from time import time

def chunks(l, n):
    """ Yield successive n-sized chunks from l. """
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

processes = [Process(target=f, args=(i,)) for i in params]
exit_codes = []
for five_processes = chunks(processes, 5):
    for p in five_processes:
        p.start()
    time_waited = 0
    start = time()
    for p in five_processes:
        if time_waited >= 100:
            p.join(0)
            p.terminate()
        p.join(100 - time_waited)
        p.terminate()
        time_waited = time() - start
    for p in five_processes:
        exit_codes.append(p.exit_code)

Вам нужно получить возвращаемые значения через что-то вроде: могу ли я получить возвращаемое значение из многопроцессорной обработки?

Коды выхода процессов равны 0, если процессы завершены и отличны от нуля, если они были завершены.

Методы из: Присоединитесь к группе процессов python с тайм-аутом. Как вы разбиваете список на куски равномерного размера?


В качестве другого варианта вы можете просто попытаться использовать apply_async для многопроцессорной обработки.

from multiprocessing import Pool, TimeoutError
from time import sleep    

if __name__ == "__main__":
    pool = Pool(processes=5)
    processes = [pool.apply_async(f, [i]) for i in params]
    results = []
    for process in processes:
        try:
            result.append(process.get(timeout=100))
        except TimeoutError as e:
            results.append(e)

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

  • 0
    Первое решение заставляет вас ждать 100 секунд, даже если все процессы завершатся за 5 секунд. Вы, вероятно, захотите цикл, который спит в течение нескольких секунд, а затем проверяет, запущен ли какой-либо из процессов, когда вернетесь в спящий режим, если он есть.
  • 0
    @ Дано да, писал быстрый ответ. Обновлено, чтобы использовать лучшую логику
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню