Python time.sleep — никогда не просыпаться

Я думаю, что это будет одна из тех простых проблем, когда вы это видите, но это сбило меня с толку.

[СТОП НАЖМИТЕ: я был прав. Решение было найдено. См. ответы.]

Я использую Python unittest framework для тестирования многопоточного приложения. Красиво и прямолинейно - у меня есть около 5 рабочих потоков, отслеживающих общую очередь, и один поток-производитель, создающий для них рабочие элементы. Поток-производитель запускается тестовым примером.

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

К фрагменту кода относится:

 logging.info("Sleep starting")
 time.sleep(5)
 logging.info("Waking up")

Теперь самое странное. Я вижу сообщение журнала «Начало сна», но не вижу сообщение о пробуждении. Программа зависает и не реагирует на прерывание клавиатуры (CTRL+C). Загрузка процессора очень низкая.

Я вижу ту же проблему в Windows и Ubuntu (Python 2.6.2).

Я подумал, возникает ли исключение и скрывается ли оно, поэтому я добавляю «печать 1/0» между первой и второй строкой - я вижу, что возникает ошибка «Деление на ноль». Я перемещаю его после сна, и я никогда не вижу сообщения.

Я подумал: «Хорошо, может быть, другой поток пытается одновременно зарегистрировать что-то очень-очень большое, и он все еще буферизуется. Что он делает?»

Что ж, к этому времени тест вернулся к модульному тесту, где он приостанавливается, ожидая начала работы потока, прежде чем проверять состояние системы.

 logging.info("Test sleep starting")
 time.sleep(0.25)
 logging.info("Test waking up")

Вау, это выглядит знакомо. Замерзает точно так же! Первое сообщение журнала появляется, второе нет.

Недавно я значительно переписал модуль, поэтому я не могу утверждать, что «ничего не трогал», но я не вижу ничего плохого в своих изменениях.

Подозрительные области:

  • Я включаю использование Threading.Lock (потому что я не знаю, как рассуждать о безопасности GIL, поэтому я придерживаюсь того, что знаю. Я не вижу ничего «тупикового» в моем коде.

  • Я новичок в Python unittest framework. Есть ли что-то, что он делает с перенаправлением ведения журнала или подобным, что может имитировать эти симптомы?

  • Нет, я не подменял нестандартный модуль времени!

Что помешает потоку проснуться? Что еще я пропустил?


person Oddthinking    schedule 26.02.2010    source источник
comment
возможно, просто используйте сон (сек). (из time import sleep) Я не пользователь Python - только что использовал Google и не могу себе представить, что команда time.sleep(sec) может заморозить всю программу, а не просто оставаться на 5 секунд ... может быть, проблема существует в другом месте? В разных темах что-ли..   -  person TheChange    schedule 26.02.2010
comment
@Изменение. Я использую точно такую ​​же функцию; просто тривиальная разница в пространстве имен. Мне тоже трудно поверить, что time.sleep() может заморозить всю программу (или, по крайней мере, два рассматриваемых потока), поэтому я прошу подсказок относительно того, где еще я должен искать.   -  person Oddthinking    schedule 26.02.2010
comment
Два предложения: либо обезьяна-патч time.sleep к фиктивной функции, либо обезьяна-патч logging.info к чему-то, что выполняет простую печать. Попробуйте либо то, и другое, и посмотрите, есть ли какие-либо полезные изменения в поведении. Также, возможно, попробуйте удерживать Ctrl-C (автоповтор) во время мониторинга ЦП и посмотрите, сможете ли вы вообще обнаружить какую-либо активность в этом процессе ... что предполагает, что он действительно жив, но что-то поглощает сигнал.   -  person Peter Hansen    schedule 26.02.2010
comment
@Peter Hansen, только что попробовал тест CTRL-C в Windows. ЦП остался на 0%. Это мой код, поэтому я могу просто удалить проблемные строки напрямую. Попробую это. У печати плохое поведение буферизации при юнит-тестах, поэтому я придерживался логирования.   -  person Oddthinking    schedule 27.02.2010
comment
Ответ найден. Спасибо за вашу помощь.   -  person Oddthinking    schedule 27.02.2010


Ответы (2)


Вздох.

Рабочий поток № 1 спит, а затем просыпается. Затем он регистрирует сообщение пробуждения и блокируется. Только один поток может вести журнал одновременно.

Поток UnitTest спит, а затем просыпается. Затем он регистрирует сообщение пробуждения и блокируется. Только один поток может вести журнал одновременно.

Рабочий поток, не упомянутый ранее в вопросе №2, незаметно заканчивал обработку ПРЕДЫДУЩЕГО элемента в очереди, в то время как первый рабочий поток спал. Дошло до выписки из журнала. Одним из параметров был объект, и функция str() вызывалась неявно. В функции str() этого объекта была ошибка; он зашел в тупик, когда получил доступ к некоторым из своих элементов данных. Взаимная блокировка возникла во время обработки функцией ведения журнала, таким образом сохранив блокировку потока ведения журнала и создав впечатление, что другие потоки никогда не просыпались.

Проверка на деление на ноль ничего не изменила, потому что результатом была попытка логирования.

person Oddthinking    schedule 27.02.2010
comment
Очевидно, я не знал, что именно это происходит, но капризы пакета ведения журнала (например, блокировки) достаточно часто подводили меня, поэтому я предложил тест с print. Рад слышать, что вы его нашли! :) - person Peter Hansen; 27.02.2010
comment
@ Тим :-) Ха! Я увидел это в твиттере Неда Батчелдера и подумал, что он читает мою ленту! twitter.com/#!/talios/statuses/157760121160744960 - person Oddthinking; 01.05.2012
comment
Здравствуйте, у меня похожая проблема, не могли бы вы подойти и помочь мне? Большое спасибо! stackoverflow.com/ вопросы/21086686/ - person Judking; 14.01.2014

В Linux попробуйте изменить планировщик ввода-вывода на полностью справедливую организацию очередей (CFQ).

echo cfq > /sys/block/sda/queue/scheduler
person Denis Chumachenko    schedule 01.05.2012
comment
Добро пожаловать в StackOverflow. Если вы посмотрите на вопрос, вы увидите, что (а) это произошло и в Windows, поэтому это решение только для Unix не будет работать. (б) проблема была с потоками Python; Python имеет собственное планирование потоков, которое не будет исправлено с изменениями в планировщике уровня ОС. (c) Я уже нашел проблему, и это была тупиковая ситуация, которую нельзя исправить изменением планировщика. - person Oddthinking; 01.05.2012