Код 1С ушёл в бесконечный цикл: как остановить его и найти причину
Содержание
Выйти из цикла в 1С можно оператором Прервать, перейти к следующему шагу — Продолжить, а досрочно выйти из условия Если внутри процедуры — оператором Возврат. Если же программа уже зависла в условии или цикле, попробуйте Ctrl+Break, затем отладчик (Отладка → Прервать), и только потом — завершение сеанса в консоли кластера или процесса в Диспетчере задач.
Типичная картина: бухгалтер запустил обработку перепроведения, курсор крутится десятую минуту, кнопки не нажимаются. Программист открывает модуль и видит Пока ... Цикл, условие выхода из которого никогда не становится ложным. Ниже — как выйти из такой ситуации без потери данных и как найти строку, где код застрял.
Операторы выхода из цикла и условия во встроенном языке
Встроенный язык 1С даёт три штатных способа покинуть блок кода. Путать их нельзя: каждый работает на своём уровне.
| Оператор | Где работает | Что делает |
|---|---|---|
Прервать | только внутри Для, Для Каждого, Пока | Выходит из ближайшего цикла, выполнение идёт после КонецЦикла |
Продолжить | только внутри цикла | Пропускает остаток тела и переходит к следующей итерации |
Возврат | в процедуре или функции | Выходит из всей процедуры; в функции — с возвратом значения |
ВызватьИсключение | где угодно | Прерывает выполнение с ошибкой, ловится в Попытка ... Исключение |
Отдельного оператора «выйти из Если» нет. Блок Если ... КонецЕсли либо выполняется целиком, либо пропускается. Если нужно остановить обработку при неверном условии, используйте Возврат в начале процедуры:
Процедура ПроверитьДокумент(Документ)
Если Не ЗначениеЗаполнено(Документ.Контрагент) Тогда
Сообщить("Не заполнен контрагент");
Возврат;
КонецЕсли;
// дальше код выполняется, только если контрагент заполнен
КонецПроцедуры
Для цикла с поиском первого подходящего значения — Прервать:
Найдена = Ложь;
Для Каждого Строка Из Товары Цикл
Если Строка.Количество = 0 Тогда
Продолжить;
КонецЕсли;
Если Строка.Сумма > Лимит Тогда
Найдена = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
💡 Прервать выходит только из одного, ближайшего цикла. Во вложенных циклах заведите флаг и проверяйте его во внешнем цикле, либо вынесите вложенный обход в отдельную функцию и выходите из неё через Возврат.
В языке есть и Перейти ~Метка;, но код с переходами по меткам тяжело читать и сопровождать. В типовых конфигурациях его практически не встретишь — не стоит начинать.
Как прервать зависшее выполнение кода в 1С:Предприятии
Если программа уже «думает» и не отвечает, действуйте от мягкого способа к жёсткому.
Шаг 1. Ctrl+Break
- Переключитесь в окно 1С.
- Нажмите
Ctrl+Break(на ноутбуках без Break часто работаетCtrl+Fn+BилиCtrl+Pause— зависит от клавиатуры). - Если код на клиенте вызывает
ОбработкаПрерыванияПользователя(), программа спросит, прервать ли выполнение. Ответьте «Да».
Надёжно это работает в клиентских циклах, где программист предусмотрел проверку прерывания. Серверный вызов так не остановить: клиент просто ждёт ответа сервера.
Шаг 2. Подождать и оценить
Долгая обработка — не всегда зависание. Перепроведение документов за год в крупной базе может идти часами. Признаки настоящего зависания:
- 🔄 индикатор прогресса (если он есть) не двигается больше 10–15 минут;
- 📈 процесс
1cv8c.exeилиrphost.exeзанимает ядро процессора на 100% без изменений; - 💾 растёт потребление памяти процессом — частый признак цикла, который копит данные в таблицу значений или массив.
Шаг 3. Закрыть процесс клиента
Если окно не отвечает, а данные не жалко потерять:
- Откройте Диспетчер задач (
Ctrl+Shift+Esc). - Найдите
1cv8c.exe(тонкий клиент) или1cv8.exe(толстый клиент, Конфигуратор). - Выберите «Снять задачу».
Незавершённая транзакция при этом откатывается — всё, что программа успела изменить внутри неё, не сохранится. В клиент-серверном варианте закрытие клиента не всегда останавливает серверный код сразу: сеанс может ещё какое-то время висеть в кластере.
Если интерфейс заблокирован полностью
Бывает, что зависает не одно окно, а вся работа: другие пользователи не могут провести документы, база «встала».
Файловая база. Закройте зависший клиент через Диспетчер задач на том компьютере, где он запущен. Если процесс работает у другого пользователя — попросите его или подключитесь к его сеансу. Файл 1Cv8.1CL в каталоге базы удаляйте только тогда, когда точно никто не работает
, иначе можно повредить данные у тех, кто ещё подключён.
Клиент-серверная база . Сеанс завершают на сервере:
- Откройте консоль администрирования серверов 1С.
- Разверните кластер →
Информационные базы→ нужная база →Сеансы. - Найдите сеанс зависшего пользователя. Ориентируйтесь на колонки с текущим временем вызова и временем в СУБД — у зависшего сеанса они большие и растут.
- В контекстном меню выберите «Удалить».
То же самое умеет утилита rac: rac session list показывает сеансы, а точный синтаксис завершения смотрите в rac help session.
Удаление сеанса обрывает работу пользователя без сохранения. Предупредите его заранее. Перезапуск службы агента сервера 1С оборвёт сеансы всех пользователей — это крайняя мера, на время, когда в базе никого нет.
Почему код зависает в цикле или условии
Бесконечный цикл почти всегда означает, что условие выхода никогда не выполняется. Классическая ошибка — Продолжить до изменения счётчика:
Сч = 0;
Пока Сч < 10 Цикл
Если ПропуститьСтроку(Сч) Тогда
Продолжить; // Сч не увеличился — цикл вечный
КонецЕсли;
Сч = Сч + 1;
КонецЦикла;
Исправление — увеличивать счётчик в начале тела:
Сч = 0;
Пока Сч < 10 Цикл
Сч = Сч + 1;
Если ПропуститьСтроку(Сч) Тогда
Продолжить;
КонецЕсли;
// обработка
КонецЦикла;
Другие частые сценарии:
- 🔁 Изменение коллекции во время обхода. Цикл добавляет строки в ту же таблицу, по которой идёт, и конец не наступает никогда.
- ♻️ Рекурсия без выхода. Например, в обработчике
ПриЗаписиобъекта вызываетсяЗаписать()того же объекта. Каждая запись запускает новую, пока сеанс не упадёт с ошибкой нехватки памяти или стека. - ⏱ Обработчик ожидания.
ПодключитьОбработчикОжиданияв обработчике, который сам же этот обработчик подключает снова, без отключения — форма постоянно занята. - 🗄 Тяжёлый запрос. Соединение таблиц без условий даёт миллионы строк. Код не зациклен, он ждёт СУБД.
- 🔒 Ожидание блокировки. Транзакция ждёт данные, которые держит другой сеанс. Подробнее — в разделе про сервер.
- 🌐 Внешний ресурс. HTTP-запрос или COM-соединение без таймаута ждёт ответа, который не придёт.
Таблица симптомов и вероятных причин
Ориентир, чтобы быстро понять, куда смотреть:
| Симптом | Вероятная причина | Что проверить |
|---|---|---|
| Процессор 100%, память не растёт | Цикл с неизменным условием | Счётчик и условие Пока, место Продолжить |
| Процессор 100%, память растёт | Цикл пополняет коллекцию | Добавление строк в обходимую таблицу или массив |
| Процессор почти не загружен, клиент ждёт | Ожидание блокировки или внешнего ресурса | Сеансы и блокировки в консоли кластера, таймауты соединений |
| Высокая нагрузка на сервер СУБД | Тяжёлый запрос | Текст запроса, условия соединений, отборы |
| Через ~20 секунд ошибка о времени ожидания блокировки | Другой сеанс держит данные в транзакции | Кто держит блокировку, длительность его транзакции |
| Клиент закрывается аварийно после долгой работы | Бесконечная рекурсия | Стек вызовов: одна и та же процедура много раз подряд |
Отладчик: как найти строку, где код застрял
Самый быстрый путь к причине — поймать зависание под отладчиком.
Остановка на текущей строке
- Запустите 1С:Предприятие из Конфигуратора командой
Отладка → Начать отладку(F5). - Воспроизведите действие, после которого программа зависает.
- Когда зависание началось, переключитесь в Конфигуратор и выберите
Отладка → Прервать. - Отладчик остановится на строке, которая выполняется прямо сейчас. Откройте стек вызовов — он покажет всю цепочку процедур.
Для клиент-серверной базы сервер должен быть запущен с разрешённой отладкой (служба агента с ключом -debug). Без этого серверный код в отладчике не остановится.
Пошаговое выполнение и табло
Встав в цикл, пройдите несколько итераций клавишами F10 (шаг через) и F11 (шаг в процедуру). Добавьте на табло переменную-счётчик и условие выхода. Если от итерации к итерации значения не меняются — причина найдена.
Точка останова с условием
Когда цикл зависает не сразу, а, скажем, на 50 000-й строке, обычная точка останова бесполезна. Поставьте точку останова с условием: например, Сч > 49990. Отладчик остановится только тогда, когда условие станет истинным.
💡 Если отладчик раз за разом останавливается на одной и той же строке и «не выходит из условия», проверьте список точек останова (Отладка → Список точек останова) — возможно, там осталась забытая точка с условием. Отключите её или удалите.
Замер производительности
Если код не зациклен, а просто медленный, включите Отладка → Замер производительности и выполните операцию. В результатах видно, какие строки выполнялись чаще всего и сколько времени заняли. Строка с сотнями тысяч вызовов — кандидат на проблему.
Серверные блокировки: когда код ждёт чужую транзакцию
В клиент-серверной базе код часто «висит» не из-за ошибки в цикле, а потому что ждёт данные, занятые другим сеансом.
При управляемых блокировках ожидание ограничено таймаутом — по умолчанию около 20 секунд. После этого пользователь получает ошибку Превышено максимальное время ожидания предоставления блокировки. Если два сеанса ждут друг друга, возникает взаимоблокировка, и платформа откатывает транзакцию одного из них.
Что делать:
- В консоли кластера найдите сеанс, который давно выполняет вызов и держит транзакцию.
- Выясните у пользователя, что он запустил. Часто это закрытие месяца, перепроведение или групповая обработка.
- Если операцию можно повторить позже — завершите сеанс (с предупреждением). Его транзакция откатится, блокировки снимутся.
- Для систематического разбора включите технологический журнал (файл
logcfg.xml) с событиямиTLOCK,TTIMEOUTиTDEADLOCK. Он покажет, какой сеанс, какие данные и сколько времени держал.
Технологический журнал на нагруженном сервере быстро растёт. Включайте его на ограниченное время и с отбором только нужных событий.
Как не допустить зависаний: профилактика в коде
Несколько привычек, которые снимают большую часть проблем.
- ✅ Страховочный счётчик в
Пока. Если число итераций заранее неизвестно, ограничьте его разумным максимумом. - ✅ Проверка прерывания в клиентских циклах. Вызов
ОбработкаПрерыванияПользователя()позволяет пользователю остановить обработку поCtrl+Break. - ✅ Не меняйте коллекцию, которую обходите. Складывайте новые строки во временную таблицу и добавляйте после цикла. Удаляйте строки обратным циклом по индексу.
- ✅ Короткие транзакции. Не выполняйте внутри транзакции долгие расчёты, диалоги и обращения к внешним сервисам.
- ✅ Запросы вместо обращений через точку в цикле. Получайте данные одним запросом до цикла.
- ✅ Таймауты для внешних соединений. Задавайте таймаут в
HTTPСоединениеи аналогичных объектах.
Пример страховочного счётчика:
МаксИтераций = 100000;
Итерация = 0;
Пока Не Готово Цикл
Итерация = Итерация + 1;
Если Итерация > МаксИтераций Тогда
ВызватьИсключение "Превышено допустимое число итераций";
КонецЕсли;
// основная обработка, которая устанавливает Готово = Истина
КонецЦикла;
Удаление строк обратным циклом — без пересчёта границы от изменяемой таблицы:
Всего = Таблица.Количество();
Для Сч = 1 По Всего Цикл
Строка = Таблица[Всего - Сч];
Если Строка.Сумма = 0 Тогда
Таблица.Удалить(Строка);
КонецЕсли;
КонецЦикла;
Индексы идут от Всего - 1 до 0, и удаление строки не сдвигает те, что ещё не обработаны.
Частые вопросы
Можно ли выйти сразу из двух вложенных циклов одним оператором?
Прервать выходит только из ближайшего цикла. Используйте флаг, который проверяется во внешнем цикле, или вынесите циклы в функцию и выйдите через Возврат.Чем `Возврат` отличается от `Прервать`?
Прервать завершает только цикл, после него выполняется код процедуры дальше. Возврат завершает всю процедуру или функцию.Почему Ctrl+Break не останавливает обработку?
ОбработкаПрерыванияПользователя(). Тогда остаются отладчик, завершение сеанса в консоли кластера или закрытие процесса клиента.