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
