Как реализовать паузу при выполнении кода в 1С без внешних компонент?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С:Предприятие часто возникает необходимость приостановить выполнение программного кода на определенное время. Такая потребность регулярно встречается при интеграции со сторонними сервисами (например, чтобы соблюдать лимиты на количество запросов в секунду к внешнему API), при организации многопоточной обработки данных, распределении нагрузки на сервер при пакетном проведении документов или при проектировании фоновых заданий, работающих в режиме демона. Исторически в платформе 1С отсутствовал простой и универсальный кроссплатформенный метод Sleep, что вынуждало разработчиков изобретать различные обходные пути.

В рамках этой статьи мы подробно разберем все существующие способы реализации задержки выполнения кода без использования внешних компонент и COM-объектов. Мы проанализируем как классические «костыли», так и современные штатные механизмы платформы 1С, выявим их достоинства, критические недостатки и особенности применения в различных операционных системах.

Способ 1. Официальное штатное решение — метод ВызватьПаузу (начиная с платформы 8.3.25)

Начнем с самой важной и долгожданной новости для всех разработчиков 1С. Начиная с версии платформы 8.3.25, фирма «1С» добавила полноценный встроенный метод для остановки текущего потока выполнения.

Синтаксис этого метода крайне прост:

ВызватьПаузу(<Интервал>)

Параметр Интервал передается в миллисекундах (например, чтобы приостановить выполнение кода на 5 секунд, необходимо передать значение 5000).

Однако при использовании данного метода необходимо учитывать одно важнейшее архитектурное ограничение платформы: метод доступен только на стороне сервера (в фоновых заданиях, регламентных заданиях, Web- и HTTP-сервисах и т. д.). Более того, разработчики платформы заблокировали возможность прямого клиент-серверного вызова для этого метода. Если вы попытаетесь вызвать с клиента серверную процедуру, внутри которой будет выполняться ВызватьПаузу, система сгенерирует исключение:

«Нельзя вызвать метод ВызватьПаузу в клиент-серверном вызове»

Это сделано осознанно, чтобы предотвратить случайное «замораживание» пользовательского интерфейса при синхронных вызовах сервера. Таким образом, использовать данный метод можно исключительно в чисто серверном контексте, например, в фоновом задании:


&НаСервере
Процедура ВыполнитьОбработкуДанныхВФоне()
    Для Каждого ПакетДанных Из СписокПакетов Цикл
        ОбработатьПакет(ПакетДанных);
        // Приостанавливаем поток на 3 секунды перед обработкой следующего пакета
        ВызватьПаузу(3000);
    КонецЦикла;
КонецПроцедуры

Способ 2. Кроссплатформенный метод БСП через фоновые задания (для старых версий платформ)

Если проект развернут на платформе версии ниже 8.3.25, но в конфигурации используется Библиотека стандартных подсистем (БСП), мы можем воспользоваться изящным и абсолютно безопасным решением, которое заложено в общем модуле ОбщегоНазначенияБТС (или аналогичных механизмах БСП) — процедурой Пауза(Секунд).

Принцип работы этого механизма построен на хитрости с фоновыми заданиями:

  1. Вызывающий поток запускает фоновое задание, передавая ему задачу выполнить паузу.
  2. Внутри самого фонового задания вызывается метод ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд), где в качестве целевого фонового задания передается оно само (или заведомо несуществующий идентификатор).
  3. Поскольку фоновое задание не может дождаться собственного завершения, операционная система переводит поток в режим ожидания (сна) на указанное количество секунд. При этом процессор абсолютно не нагружается.
  4. Основной (родительский) поток параллельно ожидает завершения этого фонового задания с тем же таймаутом. Как только время истекает, управление возвращается в основной код.

Этот способ является наиболее надежным, стабильным и кроссплатформенным (одинаково успешно работает как под управлением Windows, так и под Linux) для версий платформ от 8.3.5 до 8.3.24.

Способ 3. Имитация задержки через сетевой таймаут (HTTPСоединение или SMTP)

Рассмотрим метод, предложенный автором темы на форуме. Он основан на попытке установить сетевое соединение с заведомо недоступным адресом и ожидании системного таймаута. Автор предложил использовать ИнтернетПочтовыйПрофиль и отправку запроса на SMTP-порт публичного DNS 8.8.8.8:


Процедура ПаузаSMTP(ДлительностьВСекундах) Экспорт
    Если ДлительностьВСекундах > 0 Тогда
        Попытка
            Профиль = Новый ИнтернетПочтовыйПрофиль;
            Профиль.АдресСервераSMTP = "8.8.8.8";
            Профиль.Таймаут = ДлительностьВСекундах;
            Профиль.ПортSMTP = 25; 
            Почта = Новый ИнтернетПочта;
            Почта.Подключиться(Профиль);
        Исключение
            // Игнорируем ошибку неподключения по таймауту
        КонецПопытки;
    КонецЕсли;
КонецПроцедуры

Проанализируем этот алгоритм. Он действительно работает в некоторых средах, но имеет критические недостатки:

  1. Если на сервере или у провайдера закрыт исходящий трафик по 25 порту (что является стандартом безопасности во многих корпоративных сетях), или если брандмауэр мгновенно возвращает пакет с флагом RST (Reset) или ошибку ICMP Port Unreachable, то исключение сгенерируется мгновенно, и паузы не произойдет.
  2. Запросы к внешним ресурсам интернета (таким как 8.8.8.8) из закрытого серверного контура могут блокироваться службой безопасности предприятия.

Гораздо более стабильным, быстрым и безопасным решением является использование объекта HTTPСоединение, направленного на локальный адрес обратной петли 127.0.0.1 (localhost) и заведомо свободный порт (например, 9999). Этот способ не требует обращения к внешнему интернету и не зависит от почтовых протоколов.

Рассмотрим пример реализации паузы через локальное HTTP-соединение:


Процедура ПаузаЧерезHTTP(КоличествоСекунд) Экспорт
    Если КоличествоСекунд <= 0 Тогда
        Возврат;
    КонецЕсли;
    
    Попытка
        // Создаем соединение к неиспользуемому локальному порту с нужным таймаутом
        Соединение = Новый HTTPСоединение("127.0.0.1", 9999, , , , КоличествоСекунд);
        Соединение.Получить(Новый HTTPЗапрос());
    Исключение
        // Ошибка таймаута подключения ожидаема, мы ее просто перехватываем
    КонецПопытки;
КонецПроцедуры

Этот метод работает чрезвычайно стабильно, не нагружает центральный процессор и функционирует как на Windows, так и на Linux.

Способ 4. Использование системных утилит операционной системы (PowerShell, ping, sleep)

Еще один распространенный в сообществе разработчиков подход — это вызов штатных консольных утилит операционной системы с помощью метода ЗапуститьПриложение. На форуме были предложены варианты использования PowerShell и ping.

Посмотрим на пример вызова задержки через PowerShell в Windows:


ЗапуститьПриложение("powershell -command ""& {Start-Sleep -Seconds 5}""", , Истина);

В данном вызове третий параметр Истина является ключевым — он заставляет платформу 1С приостановить выполнение текущего потока и ожидать завершения работы вызванного приложения (в данном случае консоли PowerShell, которая засыпает на 5 секунд).

Проанализируем особенности этого и смежных методов запуска внешних утилит:

Способ 5. Цикл сравнения времени (Антипаттерн — ПОЧЕМУ ТАК ДЕЛАТЬ НЕЛЬЗЯ)

На форуме был упомянут еще один старый метод организации ожидания — пустой цикл со сравнением текущей даты:


// ВНИМАНИЕ: Данный код является вредоносным для серверных систем!
Процедура ПаузаЧерезЦикл(ДлительностьСекунд)
    ВремяОкончания = ТекущаяДата() + ДлительностьСекунд;
    Пока ТекущаяДата() < ВремяОкончания Цикл
        // Пустой цикл
    КонецЦикла;
КонецПроцедуры

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

При запуске пустого цикла процессор начинает выполнять миллиарды простейших операций сравнения в секунду без остановки. Это приводит к тому, что одно ядро процессора нагружается на 100%. Если на сервере терминалов или сервере приложений 1С несколько фоновых заданий или пользователей одновременно запустят такой код, сервер моментально израсходует все свободные вычислительные ресурсы. В результате это приведет к перегреву оборудования, падению производительности всей системы и зависанию сессий других пользователей. Никогда не используйте циклы ожидания на основе системного времени!

Резюме и рекомендации

Мы подробно проанализировали ключевые методы создания паузы при выполнении кода в 1С. Подведем итоги и выберем оптимальные стратегии:

  1. Если вы работаете на современной платформе версии 8.3.25 и выше в серверном контексте — всегда используйте штатную процедуру ВызватьПаузу(). Это наиболее производительное и нативное решение.
  2. Если вы используете конфигурацию на базе БСП версий ниже 8.3.25 — вызывайте метод ОбщегоНазначенияБТС.Пауза(). Он безопасен для процессора и полностью кроссплатформен.
  3. В качестве универсальной альтернативы для самописных конфигураций на старых платформах оптимально подходит метод сетевого таймаута через локальное HTTPСоединение. Он работает в любой операционной системе (Windows/Linux) и бережно относится к ресурсам сервера.
← На главную