Erlang – exemple 06

Cet article propose des bases de multiprogrammation en Erlang.

Remarques Code

Pour débuter, examinons le module echo.erl proposé à droite. On y trouve les fonctions echo/0 et youhou/2.

Leurs rôles respectifs vont comme suit :

  • La fonction echo/0 reçoit des messages sous la forme d'uplets contenant, dans l'ordre, un identifiant d'émetteur et un message. Ce message peut être n'importe quoi, mais l'atome exit (un choix arbitraire de notre part) représentera un signal d'arrêt; tout autre message sera simplement retourné à l'émetteur (un écho du message reçu, donc). Cette fonction itérera donc tant que le message exit n'aura pas été reçu
  • La fonction youhou/2 reçoit en paramètre un identifiant de processus et un message à envoyer. Elle se décline en deux versions, soit une acceptant spécifiquement l'atome exit en tant que message et une autre acceptant tout autre message (et l'affichant, pour montrer que le tout s'est bien déroulé)
-module(echo).
-export([echo/0, youhou/2]).

echo() ->
   receive
      { _, exit } ->
         true;
      { Pid, Msg } ->
         Pid ! { self(), Msg },
         echo()
   end.
   
youhou(Pid,exit) ->
	Pid ! { self(),exit };
youhou(Pid,Msg) ->
   Pid ! { self(), Msg },
   receive
      { P, M } ->
         io:format ("Reçu message ~w du processus ~w~n", [M, P])
   after 100 ->
      true
   end.

La raison pour la déclinaison spéciale de youhou/2 avec le message exit est que le destinataire (probablement echo/0) terminera alors son exécution et ne répondra pas. Une alternative aurait été de retourner le message exit avant de fermer boutique dans echo/0.

Quelques éléments nouveaux :

Remarques Code

Un exemple d'utilisation des fonctions du module echo est proposé à droite :

  • L'instruction 2> utilise la fonction spawn/1 pour instancier (à travers un fun) un processus exécutant la fonction echo/0 du module echo. L'identifiant résultant est récupéré dans la variable Pid
  • L'instruction 3> envoie au processus créé ci-dessus un texte significatif puis reçoit et affiche le message retourné. Les instructions 4> et 6> font de même avec un atome et un uplet
  • Enfin, l'instruction 7> envoie l'atome exit ce qui conclut l'exécution du serveur echo. Conséquemment, si nous exécutons ensuite l'instruction 8>, le client figera (il attendra une réponse d'un tiers qui n'existe plus)
Erlang R14B (erts-5.8.1.1) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.8.1.1  (abort with ^G)
1>  c(echo).
{ok,echo}
2> Pid = spawn(fun echo:echo/0).
<0.57.0>
3> echo:youhou(Pid, 'jaime mon prof').
Reçu message 'jaime mon prof' du processus <0.57.0>
ok
4> echo:youhou(Pid, ceci_est_un_atome).
Reçu message ceci_est_un_atome du processus <0.57.0>
ok
5> Point = { point, 3, 5 }.
{point,3,5}
6> echo:youhou(Pid, Point).            
Reçu message {point,3,5} du processus <0.57.0>
ok
7> echo:youhou(Pid, exit).
{<0.35.0>,exit}
8> echo:youhou(Pid,yo).

Il est possible, comme le montre youhou/2, d'adjoindre un bloc after à un bloc receive tel que receive .. after N ... end provoque un timeout si aucun message n'est reçu après une attente de N millisecondes. Les instructions dans le bloc after sont alors exécutées.

Examinons maintenant un SCS permettant à un processus d'exécuter des fonctions dans un autre processus.

Remarques Code

Le module rproc (car rpc est un module système) exposera les fonctions rpc/0, envoyer/2 et envoyer/3.

Le rôle de rpc/0 est de recevoir des messages composés de fonctions et des paramètres, et d'appliquer la fonction au(x) paramètre(s) puis de retourner les résultats. Dans le cas où le paramètre est une liste, rpc/0 appliquera la fonction à chacun des paramètres (par List Comprehension) et enverra à l'émetteur original la liste de résultats correspondante.

Un peu comme youhou/2 plus haut, envoyer/3 enverra une fonction et ses paramètres à un processus donné, puis affichera le résultat renvoyé par ce tiers, alors que envoyer/2 servira surtout à signaler à ce tiers qu'il est temps de se mettre au rencart.

-module(rproc).
-export([rpc/0,envoyer/2,envoyer/3]).

rpc() ->
   receive
      { _ , exit } ->
         true;
      {Pid,{Fct,Args}} when is_list(Args) ->
         Lst = [Fct(X) || X <- Args],
         Pid ! {self(),Lst},
         rpc();
      {Pid,{Fct, Arg}} ->
         Pid ! {self(), Fct(Arg)},
         rpc()
   end.

envoyer(Pid,Fct,Args) ->
   Pid ! { self(), { Fct, Args } },
   receive
      { Pid, Res } ->
         io:format("Reçu: ~w~n", [Res])
   end.

envoyer(Pid,exit) ->
   Pid ! { self(), exit }.

Quelques exemples d'utilisation de ces fonctions suivent.

Remarques Code

L'instruction 2> fait exécuter un fun au processus Pid en lui passant en paramètre la valeur 10. Le fun ayant pour rôle d'évaluer le double de la valeur du paramètre reçu, cet appel retourne 20, sans surprises. L'instruction 3> répète le test avec une autre valeur.

L'instruction 4> fait exécuter un fun au processus Pid en lui passant en paramètre la liste [1,2,3,4,5]. Le fun ayant pour rôle d'évaluer le double de la valeur du paramètre reçu, et rpc/0 offrant une version spécialisée pour des listes qui applique une fonction à chaque élément de la liste par voie de List Comprehension, cet appel retourne [2,4,6,8,10].

L'instruction 5> ferme le serveur rpc/0, puis 6> cherche à redémarrer une nouvelle instance de ce processus pour en associer l'identifiant à Pid; cette variable étant déjà associée à un Pattern (l'identifiant de l'instance précédente de rpc/0), ceci échoue.

Notez que l'évaluation de la faisabilité de faire correspondre les Patterns précède l'évaluation des expressions impliquées, donc aucun processus n'est démarré ici.

L'instruction 7>, f(Pid), signifie Forget Pid, ce qui dissocie une variable connue du shell de son Pattern. Suite à cet oubli volontaire, les instructions 8> et 9> reprennent leur sens.

 

Erlang R14B (erts-5.8.1.1) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.8.1.1  (abort with ^G)
1> Pid = spawn(fun rproc:rpc/0).
<0.47.0>
2> rproc:envoyer(Pid, fun(X) -> 2*X end, 10).
Reçu: 20
ok
3> rproc:envoyer(Pid, fun(X) -> 2*X end, 5). 
Reçu: 10
ok
4> rproc:envoyer(Pid, fun(X) -> 2*X end, [1,2,3,4,5]).
Reçu: [2,4,6,8,10]
ok
5> rproc:envoyer(Pid, exit).                          
{<0.35.0>,exit}
6> Pid = spawn(fun rproc:rpc/0).                      
** exception error: no match of right hand side value <0.53.0>
7> f(Pid).
ok
8> Pid = spawn(fun rproc:rpc/0).
<0.57.0>
9> rproc:envoyer(Pid, fun(X) -> 2*X end, [1,2,3,4,5]).
Reçu: [2,4,6,8,10]
ok
10>

Ceci vous donne un aperçu des mécanismes de communication de base du langage. Cela dit, il y a bien plus...


Valid XHTML 1.0 Transitional

CSS Valide !