Erlang - Concurrence

La programmation simultanée dans Erlang doit avoir les principes ou processus de base suivants.

La liste comprend les principes suivants -

piD = spawn (amusant)

Crée un nouveau processus simultané qui évalue Fun. Le nouveau processus s'exécute en parallèle avec l'appelant. Un exemple est le suivant -

Exemple

-module(helloworld). 
-export([start/0]). 

start() ->
   spawn(fun() -> server("Hello") end). 

server(Message) ->
   io:fwrite("~p",[Message]).

La sortie du programme ci-dessus est -

Production

“Hello”

Pid! Message

Envoie un message au processus avec l'identifiant Pid. L'envoi de message est asynchrone. L'expéditeur n'attend pas mais continue ce qu'il faisait.‘!’ est appelé l'opérateur d'envoi.

Un exemple est le suivant -

Exemple

-module(helloworld). 
-export([start/0]). 
start() -> 
   Pid = spawn(fun() -> server("Hello") end), 
   Pid ! {hello}. 

server(Message) ->
   io:fwrite("~p",[Message]).

Recevoir… fin

Reçoit un message qui a été envoyé à un processus. Il a la syntaxe suivante -

Syntaxe

receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
End

Lorsqu'un message arrive au processus, le système essaie de le comparer à Pattern1 (avec une possible garde Guard1); si cela réussit, il évalue Expressions1. Si le premier modèle ne correspond pas, il essaie Pattern2, et ainsi de suite. Si aucun des modèles ne correspond, le message est enregistré pour un traitement ultérieur et le processus attend le message suivant.

Un exemple de l'ensemble du processus avec les 3 commandes est présenté dans le programme suivant.

Exemple

-module(helloworld). 
-export([loop/0,start/0]). 

loop() ->
   receive 
      {rectangle, Width, Ht} -> 
         io:fwrite("Area of rectangle is ~p~n" ,[Width * Ht]), 
         loop(); 
      {circle, R} ->
      io:fwrite("Area of circle is ~p~n" , [3.14159 * R * R]), 
      loop(); 
   Other ->
      io:fwrite("Unknown"), 
      loop() 
   end. 

start() ->
   Pid = spawn(fun() -> loop() end), 
   Pid ! {rectangle, 6, 10}.

Les choses suivantes doivent être notées à propos du programme ci-dessus -

  • La fonction de boucle a la boucle de fin de réception. Ainsi, lorsqu'un message est envoyé, il sera traité par la boucle de fin de réception.

  • Un nouveau processus est généré qui va à la fonction de boucle.

  • Le message est envoyé au processus généré via le Pid! commande de message.

La sortie du programme ci-dessus est -

Production

Area of the Rectangle is 60

Nombre maximum de processus

Dans la concurrence, il est important de déterminer le nombre maximum de processus autorisés sur un système. Vous devriez alors être en mesure de comprendre combien de processus peuvent s'exécuter simultanément sur un système.

Voyons un exemple de la façon dont nous pouvons déterminer quel est le nombre maximal de processus pouvant s'exécuter sur un système.

-module(helloworld). 
-export([max/1,start/0]). 

max(N) -> 
   Max = erlang:system_info(process_limit), 
   io:format("Maximum allowed processes:~p~n" ,[Max]), 
   
   statistics(runtime), 
   statistics(wall_clock), 
   
   L = for(1, N, fun() -> spawn(fun() -> wait() end) end), 
   {_, Time1} = statistics(runtime), 
   {_, Time2} = statistics(wall_clock), lists:foreach(fun(Pid) -> Pid ! die end, L), 
   
   U1 = Time1 * 1000 / N, 
   U2 = Time2 * 1000 / N, 
   io:format("Process spawn time=~p (~p) microseconds~n" , [U1, U2]).
   wait() -> 
   
   receive 
      die -> void 
   end. 
 
for(N, N, F) -> [F()]; 
for(I, N, F) -> [F()|for(I+1, N, F)]. 

start()->
   max(1000), 
   max(100000).

Sur toute machine ayant une bonne puissance de traitement, les deux fonctions max ci-dessus passeront. Voici un exemple de sortie du programme ci-dessus.

Maximum allowed processes:262144
Process spawn time=47.0 (16.0) microseconds
Maximum allowed processes:262144
Process spawn time=12.81 (10.15) microseconds

Recevoir avec un timeout

Parfois, une instruction de réception peut attendre indéfiniment un message qui ne vient jamais. Cela peut être dû à plusieurs raisons. Par exemple, il peut y avoir une erreur logique dans notre programme, ou le processus qui allait nous envoyer un message peut avoir planté avant d'envoyer le message. Pour éviter ce problème, nous pouvons ajouter un délai d'expiration à l'instruction de réception. Cela définit une durée maximale pendant laquelle le processus attendra pour recevoir un message.

Voici la syntaxe du message de réception avec un délai spécifié

Syntaxe

receive 
Pattern1 [when Guard1] -> 
Expressions1; 

Pattern2 [when Guard2] ->
Expressions2; 
... 
after Time -> 
Expressions 
end

L'exemple le plus simple consiste à créer une fonction sleeper comme indiqué dans le programme suivant.

Exemple

-module(helloworld). 
-export([sleep/1,start/0]). 

sleep(T) ->
   receive 
   after T -> 
      true 
   end. 
   
start()->
   sleep(1000).

Le code ci-dessus dormira pendant 1000 ms avant de quitter réellement.

Réception sélective

Chaque processus d'Erlang a une boîte aux lettres associée. Lorsque vous envoyez un message au processus, le message est placé dans la boîte aux lettres. Le seul moment où cette boîte aux lettres est examinée est lorsque votre programme évalue une instruction de réception.

Voici la syntaxe générale de l'instruction de réception sélective.

Syntaxe

receive 
Pattern1 [when Guard1] ->
Expressions1; 

Pattern2 [when Guard1] ->
Expressions1; 
... 
after 
Time ->
ExpressionTimeout 
end

Voici comment fonctionne l'instruction de réception ci-dessus -

  • Lorsque nous entrons une instruction de réception, nous démarrons un minuteur (mais uniquement si une section après est présente dans l'expression).

  • Prenez le premier message dans la boîte aux lettres et essayez de le faire correspondre à Pattern1, Pattern2, etc. Si la correspondance réussit, le message est supprimé de la boîte aux lettres et les expressions suivant le modèle sont évaluées.

  • Si aucun des modèles de l'instruction de réception ne correspond au premier message de la boîte aux lettres, le premier message est supprimé de la boîte aux lettres et placé dans une «file d'attente de sauvegarde». Le deuxième message de la boîte aux lettres est ensuite essayé. Cette procédure est répétée jusqu'à ce qu'un message correspondant soit trouvé ou jusqu'à ce que tous les messages de la boîte aux lettres aient été examinés.

  • Si aucun des messages de la boîte aux lettres ne correspond, le processus est suspendu et sera replanifié pour exécution la prochaine fois qu'un nouveau message est placé dans la boîte aux lettres. Notez que lorsqu'un nouveau message arrive, les messages de la file d'attente de sauvegarde ne sont pas remis en correspondance; seul le nouveau message correspond.

  • Dès qu'un message a été mis en correspondance, tous les messages qui ont été placés dans la file d'attente de sauvegarde sont réentrés dans la boîte aux lettres dans l'ordre dans lequel ils sont arrivés au processus. Si une minuterie a été réglée, elle est effacée.

  • Si le minuteur s'écoule lorsque nous attendons un message, évaluez les expressions ExpressionsTimeout et remettez tous les messages enregistrés dans la boîte aux lettres dans l'ordre dans lequel ils sont arrivés au processus.