Я использую 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
, а не для каждого потока отдельно.
Каков самый простой способ получить мое желаемое поведение?
Этот ответ относится к библиотеке многопроцессорности 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 дополнительных секунд в течение времени выполнения. Более сложная логика (например, предыдущий пример) необходима для обеспечения более строгих тайм-аутов.
timeout
вы передаетеmap
истекает, он на самом деле не прерывает потоки Executor, он простоfuture.result(timeout)
вызывающий внутреннееTimeoutError
исключенияTimeoutError
. Рабочие потоки будут продолжать работать в фоновом режиме. Если вам нужно, чтобы поток был фактически завершен, вам нужна рабочая функция, чтобы проверить наличие какого-либо флага, который родитель может установить после истечения времени ожидания. Однако это может быть нелегко реализовать, в зависимости от того, что выполняет рабочая функция.