URBI - введение в urbiScript - часть 4

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<shell_4>
[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<name>
// тег связывается с выполнением 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
  • 0
  • 2 ноября 2010, 13:30
  • noonv

Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.