Состояние выполнения
Локальные и глобальные переменные, параллельные потоки, расширенная статистика и контейнеры.
Состояние проекта делится по области видимости. Выбор области определяет, увидит ли изменение только текущий поток или все параллельные потоки проекта.
Области видимости
Локальная переменная
Каждый рабочий поток получает собственное значение, заполненное начальным значением из проекта. Изменение видно последующим действиям этого же потока, но не соседним потокам.
Глобальная переменная
Все рабочие потоки обращаются к одному значению. Изменение одного потока становится доступно остальным и подходит для общего счётчика или накопленного результата.
Область выбирается у самой динамической переменной в Студии: Локальная или Глобальная. Запись обращения не меняется — в обоих случаях используется |DV|[Имя]; область задаёт владелец значения, а не текст команды.
Два потока могут одновременно иметь |DV|[Page] = 2 и |DV|[Page] = 9, не изменяя значения друг друга.
Если один поток записал |DV|[Processed] = 25, следующий читатель общей переменной увидит это значение.
Результаты парсеров, текущая строка, значение цикла и сетевой ответ принадлежат конкретному выполнению. Они не превращаются в общее состояние только потому, что используются рядом с глобальной динамической переменной.
|COUNTER| зарезервирован циклом FOR контейнера и хранит текущую итерацию этого контейнера. |CUSTOMCYCLEVALUE| принадлежит активной итерации пользовательского цикла. Ни одно из этих значений не заменяет глобальный счётчик проекта.
Изменение из параллельных потоков
Простое присваивание глобальной переменной меняет общее значение. Составная операция «прочитать → вычислить → записать» должна выполняться в критической секции, иначе два потока могут прочитать одно старое число и оба записать один и тот же результат.
Пусть Processed равно 0, и два потока одновременно увеличивают его без общей секции:
1. Оба читают ноль
Первый и второй поток получают 0 до того, как один из них успел сохранить изменение.
2. Оба вычисляют единицу
Каждый независимо получает результат 1.
3. Оба записывают единицу
Итог равен 1, хотя обработано две строки.
Правильное увеличение:
Чтение, сложение и запись находятся между enter и leave, поэтому следующий поток входит только после завершения всей операции.
Защита только строки присваивания не помогает, если старое значение было прочитано до входа в критическую секцию.
Не помещайте внутрь критической секции сетевой запрос, ожидание или большой цикл: пока секция занята, остальные потоки не могут изменить защищаемое общее состояние.
Состояние в сессии
При сохранении сессии Private Keeper делает единый снимок всех глобальных переменных, объявленных проектом. Локальные значения, результаты запросов, парсеров, циклов и отдельных потоков в снимок не входят.
При загрузке возможны два исхода:
- если набор имён глобальных переменных полностью совпадает с проектом, восстанавливаются все сохранённые значения, включая пустые и многострочные строки;
- если снимка нет или набор имён изменился, ни одно старое глобальное значение не смешивается с новым набором: все переменные получают начальные значения проекта, а программа сообщает об этом.
Файл сессии является обычным незашифрованным локальным файлом. Не считайте его защищённым хранилищем секретов.
Расширенная статистика
Расширенная статистика вычисляет настроенный текст и обновляет отображение раз в 50 миллисекунд. Команда |DEL| разделяет левую и правую колонки одной строки.
Первая строка показывает общий счётчик, вторая — два текущих входных значения через точку с запятой.
Текст до |DEL|, например Обработано.
Вычисляемое значение после |DEL|, например |DV|[Processed].
Если статистика считает данные всех потоков, используемая динамическая переменная должна быть глобальной. Локальный Processed существует отдельно у каждого потока и не может представлять общий итог проекта.
Общий счётчик
- Создайте глобальную динамическую переменную
Processedс начальным значением0. - После успешной обработки строки увеличивайте её внутри критической секции.
- В статистике выводите
Обработано|DEL||DV|[Processed].
Частота обновления в 50 миллисекунд относится только к отображению. Она не делает прибавление атомарным и не определяет, как часто проект изменяет переменную.
Контейнеры
Контейнер объединяет несколько действий и задаёт правило их повторения или входа. Внутренние запросы сохраняют собственные правила ожидаемого результата; условие контейнера не заменяет их.
WHILE
Повторяет внутренние действия, пока логическое выражение остаётся истинным.
FOR
Повторяет внутренние действия по целому диапазону и публикует текущую итерацию в |COUNTER|.
IF
Выполняет внутренние действия один раз, только если условие входа истинно.
Контейнер WHILE
Условие WHILE управляет продолжением повторов. Внутри цикла должно происходить изменение, от которого условие в конечном итоге станет ложным.
Условие |DV|[Page]|<|5, а одно из внутренних действий увеличивает Page.
Условие зависит от значения, которое контейнер не меняет; истинное условие продолжает повторять действия до внешнего ограничения или остановки.
Контейнер FOR
Диапазон записывается через |TO|:
Контейнер повторит внутренние действия от 1 до значения pagesCount, а текущий номер будет доступен как |COUNTER|. Для обратного диапазона также используется |TO|:
Не смешивайте две формы цикла. В контейнере диапазон 9|TO|1 сам задаёт убывание. В Скрипт-боксе у #FOR для убывания используется отдельная команда |DOWNTO|.
Внутренний адрес https://example.test/page/|COUNTER| получает очередной номер диапазона.
Пользовательская динамическая переменная с именем COUNTER конфликтует с зарезервированным значением цикла контейнера.
Контейнер IF
В последовательности A → Container[B, C] условие IF проверяется после A и до B. Оно видит результат непосредственно предшествующего внешнего действия A, а не первого запроса всего проекта и не будущий результат B.
1. Завершается действие A
Его результат становится текущим состоянием для условия контейнера.
2. Проверяется IF
Для входа только после хорошего результата используйте |CONCLUSION||=|1.
3. Запускаются B и C
При ложном условии контейнер пропускается. При истинном каждое внутреннее действие затем применяет собственное сохранённое правило ожидаемого результата.
Контейнер расположен сразу после нужного действия A, а условие явно проверяет оставленный им |CONCLUSION|.
Условие рассчитывает на результат первого запроса проекта, хотя между ним и контейнером уже выполнилось другое внешнее действие.