Перейти к основному содержимому
Версия: 7.0

Создание потоков (NEWTHREAD, NEWEXECUTOR)

Оператор создания нового потока позволяет выполнить действие в другом, отличным от текущего, потоке.

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

При необходимости поток может возвращать значение; это значение записывается в указанное свойство после завершения потока, при условии, что окружающий оператор пула ожидает завершения (см. ниже).

Сам по себе оператор создания нового потока не создаёт новую сессию изменений и не открывает отдельное SQL-соединение. В каком окружении исполняется тело потока, зависит от службы выполнения (см. ниже).

Где исполняется поток

Помимо самого потока полезно задать и место его исполнения — какому пулу или какому пользователю он принадлежит, нужно ли ждать его завершения. Платформа выделяет это в отдельный оператор — оператор создания новой службы выполнения: один оператор задаёт собственно поток (какое действие, с какой задержкой и периодом, куда писать результат), другой задаёт службу его выполнения (на сервере или на клиентском соединении пользователя; синхронно или асинхронно). По типу службы различаются два места исполнения, аналогично разделению в обращении к внешней системе и внутреннему вызову.

Серверное исполнение

Серверный пул потоков фиксированного размера — действия потоков выполняются на сервере приложений в его рабочих потоках и разделяют сессию изменений с вызывающим кодом. Поскольку сессии изменений не являются потокобезопасными, тело потока обычно оборачивают в новую сессию, а если нужна отдельная транзакция к базе — в новую сессию с отдельным SQL-соединением. Размер пула задаётся выражением целочисленного типа. Отложенные и периодические запуски обслуживаются серверным планировщиком пула.

Клиентское исполнение

Клиентский диспетчер на заданном соединении пользователя — действия потоков доставляются на это соединение и исполняются на сервере приложений в собственной свежей сессии изменений уровня навигатора этого соединения, не привязанной к какой-либо открытой форме. Сессия вызывающего кода не наследуется, поэтому оборачивать тело в новую сессию не требуется. Эта форма позволяет внутри потока использовать операторы взаимодействия с пользователем, в том числе открывать формы в интерактивном представлении — они направляются в UI этого соединения; открытая таким потоком форма получает свою собственную сессию по обычным правилам. Отложенные и периодические запуски обслуживаются клиентским таймером в браузере или desktop-клиенте.

Синхронизация

Независимо от места исполнения оператор службы поддерживает два режима синхронизации. В синхронном режиме он дожидается завершения вложенных потоков, для которых регистрируется ожидающий future, и записывает их возвращаемые значения в свойства текущей сессии; в асинхронном — завершается сразу после диспетча. Для синхронного режима может быть задан тайм-аут ожидания; если какие-то потоки в него не уложились, оператор выбрасывает ошибку, но значения, записанные потоками, успевшими завершиться ранее, всё равно сохраняются и доступны в окружающем TRY ... CATCH.

Язык

Для объявления действия, выполняющего другое действие в новом потоке, используется оператор NEWTHREAD. Для создания службы выполнения и группировки вложенных NEWTHREAD используется оператор NEWEXECUTOR.

Примеры

testNewThread () {
// Серверный пул потоков с ожиданием завершения
NEWEXECUTOR {
FOR id(Sku s) DO {
NEWTHREAD {
NEWSESSION {
name(s) <- STRING[20](id(s));
APPLY;
}
}
}
} THREADS 10 WAIT;

// Показ сообщения всем остальным подключённым пользователям
FOR user(Connection conn) AND connectionStatus(conn) == ConnectionStatus.connectedConnection AND conn != currentConnection() DO {
NEWEXECUTOR { NEWTHREAD MESSAGE 'Сообщение'; } CLIENT conn NOWAIT;
}

// Периодическое выполнение на стороне клиента — пока соединение живо
NEWEXECUTOR {
NEWTHREAD MESSAGE 'tick'; SCHEDULE PERIOD 10000 DELAY 5000;
} CLIENT currentConnection() NOWAIT;

// Сбор результата из потока с тайм-аутом ожидания
LOCAL res = INTEGER ();
TRY {
NEWEXECUTOR {
NEWTHREAD { RETURN computeHeavy(); } TO res;
} THREADS 1 WAIT 5000;
} CATCH {
MESSAGE 'не уложились в тайм-аут: ' + messageCaughtException();
}
}