среда, 24 ноября 2010 г.

IPC::MPS - Message Passing Style of Inter-process communication

Немного на русском о межпроцессном взаимодействии в стиле передачи сообщений.

IPC::MPS, - система обмена сообщениями между родительскими и дочерними процессами, а также между дочерними, имеющими общего родителя.

use IPC::MPS;

my $vpid = spawn {
receive {
msg ping => sub {
my ($from, $i) = @_;
print "Ping ", $i, " from $from\n";
snd($from, "pong", $i);
};
};
};

snd($vpid, "ping", 1);
receive {
msg pong => sub {
my ($from, $i) = @_;
print "Pong $i from $from\n";
if ($i < 3) {
snd($from, "ping", $i + 1);
} else {
exit;
}
};
};

Concurrency programming

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

Сообщения передаются посредством UNIX сокетов.

$vpid = spawn {
...
receive {
msg "name 1" => sub {
my ($from, @args) = @_;
...
};
msg "name 2" => sub { ... };
msg "name 3" => sub { ... };
...
};
};

Создание дочерних процессов происходит не при вызове spawn, а потом, при receive, непосредственно перед вызовом цикла отправки-приема сообщений. Это необходимо чтобы все vpid были определены до вызовов fork. vpid - адрес ссылки на сокет с главного процесса в дочерний.

Внутри spawn можно делать другие spawn. Если spawn делается внутри receive, то надо вызвать и receive, чтобы запустить дочерние процессы. При этом новый receive добавит свою информацию к старой и передаст управление циклу передачи сообщений старого receive.

Отправка сообщений.

snd($vpid, "msg name", @args);

Если vpid равен 0, то это сообщение родительскому процессу.

Если дочерний процесс видит, что родительский завершился, то он также завершается.

Dataflow programming

Иногда при обработке сообщения может возникнуть ситуация, когда необходимо получить дополнительную информацию от других процессов, и лишь затем продолжить обработку сообщения. Для этого можно послать сообщения с запросом информации, а затем в нужном месте дождаться получения информации при помощи подпрограммы wt (сокращение от wait), не прерывая обработку текущего сообщения.

snd("vpid_1", "msg_1", @args_1);
snd("vpid_2", "msg_2", @args_2);

my $r = wt("vpid_1", "msg_1");
...
my @r = wt("vpid_2", "msg_2");
...

Подпрограмма wt запускает новый цикл ожидания, продожается отправка еще не отправленных и прием новых сообщений, но новые сообщения не обрабатываются, а накапливаются в буфере. Когда получен ответ на необходимое сообщение, этот цикл ожидания завершается и wt возвращает ответ - обработка текущего сообщения продолжается.

my $r = snd_wt($vpid, $msg, @args);

является сокращением для:

snd($vpid, $msg, @args);
my $r = wt($vpid, $msg);


The main differences from Erlang

Внимание, это не Erlang, это Perl IPC::MPS. Основные отличия, вытекающие одно из другого:

1. Полноценные процессы операционной системы.
2. Подпрограмма spawn непосредственно не создает процессы, а лишь осуществляет подготовительные операции. Процессы создаются при вызове receive.
3. "receive" - "многоразовый", а не "одноразовый", как в Erlang.
4. "receive" внутри "receive" не заменяет временно собой предыдущий, а добавляет новые обработчики сообщений и запускает новые процессы.
5. Чтобы дождаться внутри обработчика сообщения ответ на конкретное сообщение следует использовать подпрограмму wt. В Erlang это делается все тем-же "receive".

Распределенное программирование

Чтобы сделать текущий процесс узлом необходимо вызвать подпрограмму listener:

listener($host, $port);

Подключение к уделенному узлу осуществляется при помощи подпрограммы open_node:

my $vpid = open_node($host, $port);

Чтобы обнаружить закрытие соединения следует определить обработчик сообщения NODE_CLOSED:

msg NODE_CLOSED => sub {
my ($vpid) = @_;
...
};

Это утверждение справедливо как для клиента, так и для сервера.

Совместимость с модулями, основанными на Event, EV и AnyEvent

IPC::MPS::Event, IPC::MPS::EV позволяют использовать сторонние модули на основе модулей Event и EV соответственно (напрямую или через AnyEvent).

P.S.
Примеры смотрите в каталоге demo.