Как усыпить Python: полный гид по time.sleep(), asyncio.sleep() и паузам в потоках
Задержки в Python — не только time.sleep(). В статье объясняем, как правильно ставить паузы в синхронном коде, потоках, асинхронных корутинах и GUI-приложениях, чтобы не блокировать программу.
Иногда Python должен подождать. Задержка между запросами к API, пауза перед повторной проверкой сервера, ритмичный вывод в терминал — за всё это отвечает функция time.sleep(). Она кажется элементарной, но в разных контекстах — синхронном коде, потоках, асинхронности и GUI — пауза устроена по-разному. Разбираем, как не «усыпить» приложение.
Что делает time.sleep()
Функция time.sleep(seconds) приостанавливает выполнение текущего потока на указанное время. Аргумент — число секунд, включая дробные значения. Например, 0.001 — это одна миллисекунда, а 1.5 — полторы секунды.
Важный нюанс: time.sleep() гарантирует лишь минимальную задержку. Реальная пауза почти всегда чуть дольше из-за планировщика операционной системы и текущей нагрузки. Для большинства задач это не критично, но для высокоточного тайминга лучше искать другие инструменты.
Типичные сценарии использования
Паузы чаще всего нужны в трёх случаях: чтобы не перегружать внешний сервис, чтобы дать интерфейсу время прогрузиться и чтобы сделать вывод в консоли более читаемым.
- Распределение запросов к API с ограничением rate limit.
- Повторные попытки выполнения ненадёжных операций.
- Пауза между итерациями мониторинга или парсинга.
Мониторинг сайта с интервалом в 60 секунд
Простой аптайм-бот опрашивает страницу и засыпает на минуту. Без задержки скрипт уйдёт в бесконечный цикл и может получить бан по IP.
Совет:
Для реального мониторинга добавьте логирование или уведомления, иначе ошибки останутся только в консоли.
Декоратор для повторных попыток
Ненадёжные операции — загрузка файла, запрос к внешнему API — иногда падают из-за временной проблемы. Вместо того чтобы оборачивать каждый вызов в цикл, удобно сделать декоратор, который повторяет функцию с задержкой.
Обратите внимание на raise внутри блока except: если его убрать, исключение не пойдёт наружу, и декоратор не поймёт, что нужна повторная попытка.
Паузы в потоках: Event.wait()
В потоках можно использовать time.sleep(), но он непрерываен: поток не сможет быстро завершиться, пока спит. Для корректной остановки лучше применять threading.Event().wait(). Этот метод прерывается, как только событие установлено через event.set().
Такой подход позволяет аккуратно завершить все потоки по Ctrl+C: event.set() мгновенно будит все ожидающие wait().
Неблокирующие паузы в asyncio
Асинхронный код в Python появился в версии 3.4 модуля asyncio и получил синтаксис async/await в 3.5. Внутри корутин нельзя использовать обычный time.sleep(): он заморозит весь event loop. Для паузы в асинхронном коде есть asyncio.sleep().
Ключевое отличие: asyncio.sleep() приостанавливает только текущую корутину, уступая управление event loop. Остальные задачи продолжают выполняться. Это особенно важно при соблюдении rate limit при параллельных запросах.
Как не заморозить GUI
В GUI-фреймворках весь интерфейс работает в главном потоке с собственным циклом событий. Если вызвать time.sleep() в обработчике кнопки, окно повиснет: не будет отрисовки, не сработают клики, а в Windows может появиться предупреждение о неответствии.
Метод .after() в Tkinter планирует callback через заданное число миллисекунд и сразу возвращает управление циклу событий. Аналог в wxPython — wx.CallLater(). Паттерн универсален: не блокируйте поток, планируйте отложенный вызов.
Ключевые выводы
time.sleep()— универсальный способ поставить синхронную паузу, но он блокирует поток целиком.- Для повторных попыток удобен декоратор с задержкой, который ловит исключения и делает retry.
- В потоках предпочитайте
Event.wait(): его можно прервать черезevent.set(). - В асинхронном коде используйте
asyncio.sleep(), иначе заморозите весь event loop. - В GUI применяйте фреймворковые методы вроде
Tkinter.after(), чтобы интерфейс оставался отзывчивым.
Часто задаваемые вопросы
Как сделать паузу в одну секунду?
Импортируйте модуль time и вызовите time.sleep(1). Выполнение текущего потока приостановится на одну секунду.
Можно ли задать задержку в миллисекундах?
Да, передав дробное число: time.sleep(0.5) — 500 мс, time.sleep(0.001) — 1 мс.
В чём разница между time.sleep() и asyncio.sleep()?
time.sleep() блокирует весь поток, а asyncio.sleep() только текущую корутину, позволяя event loop запускать другие задачи.
Почему GUI зависает при использовании time.sleep()?
Потому что GUI обрабатывает события и отрисовку в главном потоке. time.sleep() его блокирует, поэтому нужны фреймворковые методы отложенного вызова.
Что использовать в потоках: time.sleep() или Event.wait()?
Лучше Event.wait(), потому что его можно прервать для корректной остановки потока.
Выводы
Пауза в коде — это не просто «подождать секунду». В синхронном скрипте достаточно time.sleep(), в потоке важно уметь выходить по сигналу, в асинхронном коде нельзя блокировать event loop, а в GUI нужно работать в рамках фреймворкового цикла событий. Правильный инструмент зависит от контекста, и выбор влияет на отзывчивость и стабильность приложения.
Не блокируйте поток без причины. Если можно уступить управление — уступите.
Источник и дополнительные материалы: Real Python — Python sleep(): How to Add Time Delays to Your Code.