2011-03-29 2 views
1

В одном из моих сценариев, я использую следующий бесконечный цикл для проверки активного подключения к Интернету:Python скрипт висит в петле

def online_check(): 
    try: 
     con = urllib2.urlopen("http://www.google.com/") 
     data = con.read() 
     logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]'))) 
    except: 
     logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]'))) 
     time.sleep(3) 
     online_check() 

Я знаю, что это не очень элегантное решение, но проблема заключается в том, что когда я запускаю свой скрипт, он иногда вызывает метод online_check и застревает посередине (один раз примерно в 200 попытках). Скрипт все еще запущен и не генерируется исключение; сценарий просто застрял. Я могу нажать CTRL + C (даже после того, как часы скрипта застряли), и он просто выбросит исключение и продолжит работу со следующим online_check. Я также переписал сценарий, чтобы проверить IP-адрес в «ifconfig», а не на pinging google, к сожалению, с аналогичными результатами.

Что я делаю неправильно? Могу ли я переписать сценарий, чтобы этого не произошло? Есть ли что-то, что я могу сделать, чтобы узнать, что здесь происходит?

справка приветствуется. Btw. Я использую Python2.7.1, и я пробовал этот скрипт как на Linux, так и на Mac.

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

+1

Я удивлен, что вы не переполняете столик! –

+0

Не могли бы вы объяснить это мне Гордон? вы просто переполнили мои мозговые стоки. –

+1

См. http://en.wikipedia.org/wiki/Stack_overflow для получения дополнительной информации, но основная идея заключается в том, что каждый раз, когда вы вызываете функцию, вы должны нажимать новый кадр на стек вызовов, и это занимает память, и в итоге у вас закончится стоп вызовов и сбой. Буквально переполнение стека. Вы выбрали правильный сайт, чтобы задать этот вопрос. –

ответ

3

В дополнение к бесконечной рекурсии (CPython, насколько я знаю, не поддерживает оптимизированную рекурсию хвоста), вы не закрываете свое соединение.

Возможно, вы столкнетесь с каким-либо ограничением соединения либо с ОС, либо с Google при повторном запуске. Вероятно, это происходит до достижения максимальной глубины рекурсии, поэтому вы не получаете исключения.

Соединение, как правило, закрывается при сборе мусора, но я думаю, что из-за рекурсии переменная не полностью выходит из области действия до окончания рекурсии.

Также как упоминалось в @senderle, вы должны попытаться поймать более конкретную ошибку.

Try:

def online_check(): 
    while True: 
    try: 
     con = urllib2.urlopen("http://www.google.com/") 
     data = con.read() 
     logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]'))) 
    except urllib2.URLError: 
     logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]'))) 
     time.sleep(3) 
    finally: 
     con.close() 

(Предупреждение непроверенный код)

+0

Спасибо Дэви, я попробую! –

+0

@nick Я также добавил, что @senderle сказал о том, чтобы поймать более конкретную ошибку, потому что вы всегда должны поймать наиболее специфическую, которая работает. В противном случае вы просто скрываете другие ошибки. – Davy8

4

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

Такие, как:

def online_check(max_checks=10, current_check=0): 
    try: 
     con = urllib2.urlopen("http://www.google.com/") 
     data = con.read() 
     logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]'))) 
    except: 
     logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]'))) 
     if max_checks > current_check: 
      time.sleep(3) 
      online_check(max_checks, current_check+1) 

Так вы могли бы сделать:

online_check(5) # for 5 maximum checks 
online_check() # use the default value, which is 10 

Я также предлагаю вам поймать более конкретные исключения, для улучшения навыков кодирования, но и потому, что, когда вы делаете CTRL-C, Python выдает исключение KeyboardInterrupt, которое ваш код действительно ловит, потому что вы улавливаете все исключения.

+1

Я не уверен, что это происходит, потому что метод вызывается в среднем 4 раза, прежде чем он выйдет. Обычно он застревает только через 3 пробега. Может ли это быть связано с глубиной рекурсии? Разве не было бы исключения? –

+0

Независимо от того, вы делаете рекурсию, и вам всегда нужен надежный базовый случай, чтобы отменить рекурсию. Возможно, вы правильно указали, что urlopen() или read() просто висят, однако вы должны реализовать свой надежный базовый футляр (то есть не зависящий ни от какого исключения). –

+0

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

1

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

def online_check(): 
    while True: 
    try: 
     con = urllib2.urlopen("http://www.google.com/") 
     data = con.read() 
     logging.debug('{0} Reached the host. Exiting online_check'.format(time.strftime('[ %H:%M:%S ]'))) 
     break 
    except: 
     logging.debug('{0} Could not reach host trying again in 3 seconds'.format(time.strftime('[ %H:%M:%S ]'))) 
     time.sleep(3) 

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

+0

Никакой отдельный поток и код не выполняются после вызова этого метода. Должен ли я возвращаться после успешной онлайн-проверки? Зачем вам это нужно? –

+0

функция действительно возврат. Он возвращается, когда нет исключения. Рекурсия происходит только при сбое. – Davy8

2

В дополнение к предложениям других, вы действительно должны указать, какие исключения (ы) вы ловли. Control-C не работает, потому что это просто вызывает исключение, и ваш оператор except интерпретирует его как то же, что и все остальные.

Похоже на исключение, которое вы хотите, это urllib2.URLError.

+0

+1, потому что я не знал этого о Ctrl-C (а также потому, что это правда, вы никогда не должны просто улавливать все типы исключений) – Davy8

Смежные вопросы