1. введение в urbiScript — часть 1
2. введение в urbiScript — часть 2
3. введение в urbiScript — часть 3
4. введение в urbiScript — часть 4
Продолжаем рассмотрение возможностей скриптового языка urbiScript от компании Gostai, предназначенного для управления роботами.
urbiScript выполняется на Urbi-сервере, который запускается либо на роботе, либо на компьютере и к которому можно подключиться через сеть обычным telnet-ом.
Основной особенностью и краеугольным камнем urbiScript является параллельная работа
23. Операторы параллельной работы
Пока мы рассматривали завершение операторов и команд символом точки с запятой (;).
Однако существует и другие операторы разделения команд:
«;» — последовательно. Перед продолжением — ждать пока левый оператор завершит работу.
«&» — параллельно. Все операторы начинают работу одновременно и выполняются параллельно. Блок & завершает работу, когда все операторы завершили работу. & имеет более высокий приоритет чем другие операторы разделения.
«,» — в фоновом режиме. Левый оператор запускается и начинается выполнение правого оператора.
Этот оператор связывается с областью применения: при использовании внутри области,
область завершает работу только тогда, когда все операторы, работающие в фоновом режиме с помощью ‘,’ ,завершат работу.
«|» — один за другим. Правый оператор запускается немедленно после левого (создаётся атомарность для параллельных задач)
Пример демонстрирует использование разделителя & для запуска двух функций одновременно(параллельно).
function test(name) { echo(name + ": 1"); echo(name + ": 2"); echo(name + ": 3"); } |; // последовательное выполнение test("left") ; test ("middle"); test ("right"); [00000000] *** left: 1 [00000000] *** left: 2 [00000000] *** left: 3 [00000000] *** middle: 1 [00000000] *** middle: 2 [00000000] *** middle: 3 [00000000] *** right: 1 [00000000] *** right: 2 [00000000] *** right: 3 // параллельное выполнение test("left") & test("middle") & test ("right"); [00000000] *** left: 1 [00000000] *** middle: 1 [00000000] *** right: 1 [00000000] *** left: 2 [00000000] *** middle: 2 [00000000] *** right: 2 [00000000] *** left: 3 [00000000] *** middle: 3 [00000000] *** right: 3
Разница между «&» и «,» довольно тонкая:
* операторы связанные «&» не запустятся пока все операторы не будут известны. Т.е. написав строчку заканчивающуюся оператором «&» система будет ждать правого оператора (например «,» или «;«) и затем запустит операторы на выполнение. Оператор же заканчивающийся «,» запустится на выполнение сразу.
* выполнение блокируется после того как все операторы группа «&» завершили работу.
function test(name) { echo(name + ": 1"); echo(name + ": 2"); echo(name + ": 3"); } | {}; // запуск test() и функции echo("right") параллельно, // и ожидание пока оба завершатся перед тем как продолжить выполнение test("left") & echo("right"); echo("done"); [00000000] *** left: 1 [00000000] *** right [00000000] *** left: 2 [00000000] *** left: 3 [00000000] *** done // запуск test() в фоновом режиме, затем обе функции echo() без ожидания test("left") , echo("right"); echo("done"); [00000000] *** left: 1 [00000000] *** right [00000000] *** left: 2 [00000000] *** done [00000000] *** left: 3 // Пример "|" // параллельные задачи выполняются в перемешку { { echo("11") ; sleep(100ms) ; echo("12") }, { echo("21") ; sleep(400ms) ; echo("22") }, }; [00000316] *** 11 [00000316] *** 21 [00000316] *** 12 [00000316] *** 22 // между задачами объединёнными при помощи | другие задачи вклиниться не могут { { echo("11") | echo("12") }, { echo("21") | echo("22") }, }; [00000316] *** 11 [00000316] *** 12 [00000316] *** 21 [00000316] *** 22
23.1 Отделение — detach
Функция Control.detach запускает выполнение в фоновом режиме, как и оператор «,» ,но выполнение полностью отделено и не ожидает завершения работы области.
function test() { // ждём одну секунду, и выводим "foo". detach({sleep(1s); echo("foo")}); }|; test(); echo("Not blocked"); [00000000] Job[00000000] *** Not blocked sleep(2s); echo("End of sleep"); [00001000] *** foo [00002000] *** End of sleep
24. Теги
Тег — это объект, помещающий блок кода для внешнего контроля его выполнения. Выполнение кода может быть заморожено, возобновлено, остановлено.
Любой кусок кода можно обозначить тегом. Для этого достаточно написать имя тега и поставить двоеточие (:). Тег так же можно создать через Tag.new(«name»). Название тега опционально.
// создание нового тега var mytag = Tag.new("name"); [00000000] Tag// тег связывается с выполнением 42 mytag: 42; [00000000] 42 // тег связывается с выполнением блока mytag: { "foo"; 51 }; [00000000] 51 // тег связывается с вызовом функции mytag: echo("tagged"); [00000000] *** tagged
Можно использовать тег, который не был объявлен ранее; при этом он будет создан автоматически.
Однако это не рекомендуется, т.к. тег будет создан в глобальной области.
// Если mytag не объявлен, то это приведёт к выполнению: // var Tag.mytag = Tag.new("mytag"); mytag : 42; [00000000] 42
Теперь можно рассмотреть какие методы контроля предоставляет тег:
freeze — приостанавливает выполнение кода
unfreeze — возобнавляет выполнение приостановленного кода
stop — останавливает выполнение кода. Поток выполнения, который останавливается командой stop мгновенно перепрыгивает на конец блока кода связанного с тегом.
block — блокирует выполнение кода:
* останавливает код
* если поток выполнения встречает заблокированный код — он просто пропускается.
unblock — разблокирует код.
Примеры работы данных методов:
// в фоновом режиме запускаем код, который печатает "ping" // каждую секунду. Тег mytag используется для контроля mytag: every (1s) echo("ping"), sleep(2.5s); [00000000] *** ping [00001000] *** ping [00002000] *** ping // приостановим выполнение mytag.freeze; // больше не печатает :) sleep(1s); // возобновим работу mytag.unfreeze; sleep(1s); [00007000] *** ping
// Теперь, в случае завершения тега - распечатываем сообщение. { mytag: every (1s) echo("ping"); // этот код выполнится при остановке кода тега echo("Background job stopped")| }, sleep(2.5s); [00000000] *** ping [00001000] *** ping [00002000] *** ping // остановим выполнение mytag.stop; [00002500] *** Background job stopped // код остановлен, поэтому метод // unfreez не работает. mytag.unfreeze;
// Теперь, печатаем сообщение когда выполняем код тега loop { echo("ping"); sleep(1s); mytag: { echo("pong"); sleep(1s); }; }, sleep(3.5s); [00000000] *** ping [00001000] *** pong [00002000] *** ping [00003000] *** pong // блокируем вывод pong mytag.block; sleep(3s); // половина кода цикла больше не выполняется [00004000] *** ping [00005000] *** ping [00006000] *** ping // разблокируем pong mytag.unblock; sleep(3.5s); [00008000] *** pong [00009000] *** ping [00010000] *** pong [00011000] *** ping
24.1 Продвинутый пример использования параллельного выполнения и тегов.
В примере показано применение функции timeOut, которая принимает в качестве аргументов код для выполнения и время выполнения кода.
Функция выполняет код выполняется и возвращает его значение.
Однако, в случае если время выполнения кода превышает заданный предел — код останавливается, выводится надпись «Timeout!» и возвращается void.
В данном примере используются:
* ленивые аргументы
* операторы параллельного выполнения кода для запуска кода контроля времени в фоновом режиме
// timeout (код, продолжительность). function timeOut { // в фоновом режиме запускаем код контроля времени, // который просто ожидает заданное время перед тем как остановить функцию // call.evalArgAt(1) - второй аргумент функции - продолжительность. { sleep(call.evalArgAt(1)); echo("Timeout!"); return; }, // выполняем код и возвращаем его значение return call.evalArgAt(0); } |; timeOut({sleep(1s); echo("On time"); 42}, 2s); [00000000] *** On time [00000000] 42 timeOut({sleep(2s); echo("On time"); 42}, 1s); [00000000] *** Timeout!
Читать далее: введение в urbiScript — часть 5