Подтвердить что ты не робот

Правильный способ запускать некоторый код с тайм-аутом в Python

Я посмотрел онлайн и нашел несколько советов по обсуждению и ActiveState для запуска некоторого кода с таймаутом. По-видимому, есть несколько общих подходов:

  • Используйте поток, который запускает код, и join он с таймаутом. Если тайм-аут истек - уничтожьте поток. Это не поддерживается непосредственно в Python (используется частная функция _Thread__stop), поэтому это плохая практика
  • Используйте signal.SIGALRM - но этот подход не работает в Windows!
  • Использовать подпроцесс с таймаутом - но это тяжелый - что, если я хочу часто запускать прерывистую задачу, я не хочу, чтобы каждый из них выполнял пожар!

Итак, что такое правильный путь? Я не спрашиваю об обходных решениях (например, используйте Twisted и async IO), но фактический способ решить актуальную проблему - у меня есть функция, и я хочу запустить ее только с некоторым таймаутом. Если тайм-аут истек, я хочу вернуться обратно. И я хочу, чтобы он работал на Linux и Windows.

4b9b3361

Ответ 1

Совершенно общее решение этого действительно, честно говоря, не существует. Вы должны использовать правильное решение для данного домена.

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

  • Если вам нужны тайм-ауты, потому что вы боитесь, что код вышел из-под контроля (например, если вы боитесь, что пользователь попросит ваш калькулятор вычислить 9**(9**9)), вам нужно запустить его в другом процессе, Это единственный простой способ достаточно изолировать его. Запускать его в вашей системе событий или даже в другом потоке будет недостаточно. Также можно разбить вещи на маленькие куски, похожие на другое решение, но требует очень тщательной обработки и обычно не стоит этого; в любом случае, это не позволяет вам делать то же самое, что просто запускать код Python.

Ответ 2

Что вы можете найти, это модуль multiprocessing. Если subprocess слишком тяжелый, это может и не соответствовать вашим потребностям.

import time
import multiprocessing

def do_this_other_thing_that_may_take_too_long(duration):
    time.sleep(duration)
    return 'done after sleeping {0} seconds.'.format(duration)

pool = multiprocessing.Pool(1)
print 'starting....'
res = pool.apply_async(do_this_other_thing_that_may_take_too_long, [8])
for timeout in range(1, 10):
    try:
        print '{0}: {1}'.format(duration, res.get(timeout))
    except multiprocessing.TimeoutError:
        print '{0}: timed out'.format(duration) 

print 'end'

Ответ 3

Я нашел это с библиотекой eventlet:

http://eventlet.net/doc/modules/timeout.html

from eventlet.timeout import Timeout

timeout = Timeout(seconds, exception)
try:
    ... # execution here is limited by timeout
finally:
    timeout.cancel()

Ответ 4

Для "нормального" кода Python, который не задерживается в течение длительного времени в C-расширениях или ожиданиях ввода-вывода, вы можете достичь своей цели, установив функцию трассировки с помощью sys.settrace(), которая прерывает текущий код при достижении таймаута.

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

Ответ 5

Другой способ - использовать faulthandler:

import time
import faulthandler


faulthandler.enable()


try:
    faulthandler.dump_tracebacks_later(3)
    time.sleep(10)
finally:
    faulthandler.cancel_dump_tracebacks_later()

N.B: Модуль faulthandler является частью stdlib в python3.3.

Ответ 6

Если это связано с сетью, вы можете попробовать:

import socket
socket.setdefaulttimeout(number)

Ответ 7

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

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

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

Ответ 8

Я решил так: Для меня отлично работает (в окнах и совсем не тяжело), ​​я надеюсь, что это было полезно для кого-то)

import threading
import time

class LongFunctionInside(object):
    lock_state = threading.Lock()
    working = False

    def long_function(self, timeout):

        self.working = True

        timeout_work = threading.Thread(name="thread_name", target=self.work_time, args=(timeout,))
        timeout_work.setDaemon(True)
        timeout_work.start()

        while True:  # endless/long work
            time.sleep(0.1)  # in this rate the CPU is almost not used
            if not self.working:  # if state is working == true still working
                break
        self.set_state(True)

    def work_time(self, sleep_time):  # thread function that just sleeping specified time,
    # in wake up it asking if function still working if it does set the secured variable work to false
        time.sleep(sleep_time)
        if self.working:
            self.set_state(False)

    def set_state(self, state):  # secured state change
        while True:
            self.lock_state.acquire()
            try:
                self.working = state
                break
            finally:
                self.lock_state.release()

lw = LongFunctionInside()
lw.long_function(10)

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

Ответ 9

решение с конструкцией и слиянием 'with' от

  • Функция тайм-аута, если требуется слишком много времени для завершения
  • этот поток, который работает лучше.

    import threading, time
    
    class Exception_TIMEOUT(Exception):
        pass
    
    class linwintimeout:
    
        def __init__(self, f, seconds=1.0, error_message='Timeout'):
            self.seconds = seconds
            self.thread = threading.Thread(target=f)
            self.thread.daemon = True
            self.error_message = error_message
    
        def handle_timeout(self):
            raise Exception_TIMEOUT(self.error_message)
    
        def __enter__(self):
            try:
                self.thread.start()
                self.thread.join(self.seconds)
            except Exception, te:
                raise te
    
        def __exit__(self, type, value, traceback):
            if self.thread.is_alive():
                return self.handle_timeout()
    
    def function():
        while True:
            print "keep printing ...", time.sleep(1)
    
    try:
        with linwintimeout(function, seconds=5.0, error_message='exceeded timeout of %s seconds' % 5.0):
            pass
    except Exception_TIMEOUT, e:
        print "  attention !! execeeded timeout, giving up ... %s " % e