Erlang - Concurrencia

La programación concurrente en Erlang debe tener los siguientes principios o procesos básicos.

La lista incluye los siguientes principios:

piD = spawn (divertido)

Crea un nuevo proceso concurrente que evalúa Fun. El nuevo proceso se ejecuta en paralelo con la persona que llama. Un ejemplo es el siguiente:

Ejemplo

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

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

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

La salida del programa anterior es:

Salida

“Hello”

¡Pid! Mensaje

Envía un mensaje al proceso con el identificador Pid. El envío de mensajes es asincrónico. El remitente no espera sino que continúa con lo que estaba haciendo.‘!’ se llama operador de envío.

Un ejemplo es el siguiente:

Ejemplo

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

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

Recibe ... fin

Recibe un mensaje que se ha enviado a un proceso. Tiene la siguiente sintaxis:

Sintaxis

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

Cuando llega un mensaje al proceso, el sistema intenta compararlo con el Patrón1 (con posible guardia Guard1); si tiene éxito, evalúa Expressions1. Si el primer patrón no coincide, intenta Patrón2 y así sucesivamente. Si ninguno de los patrones coincide, el mensaje se guarda para su procesamiento posterior y el proceso espera el siguiente mensaje.

En el siguiente programa se muestra un ejemplo de todo el proceso con los 3 comandos.

Ejemplo

-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}.

Se deben tener en cuenta las siguientes cosas sobre el programa anterior:

  • La función de bucle tiene el bucle final de recepción. Entonces, cuando se envía un mensaje, el bucle de finalización de recepción lo procesará.

  • Se genera un nuevo proceso que va a la función de bucle.

  • El mensaje se envía al proceso generado a través del Pid! comando de mensaje.

La salida del programa anterior es:

Salida

Area of the Rectangle is 60

Número máximo de procesos

En concurrencia, es importante determinar el número máximo de procesos que se permiten en un sistema. Entonces debería poder comprender cuántos procesos se pueden ejecutar simultáneamente en un sistema.

Veamos un ejemplo de cómo podemos determinar cuál es el número máximo de procesos que se pueden ejecutar en un sistema.

-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).

En cualquier máquina que tenga una buena potencia de procesamiento, se aprobarán las dos funciones máximas anteriores. A continuación se muestra una salida de muestra del programa anterior.

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

Reciba con un tiempo de espera

A veces, una declaración de recepción puede esperar una eternidad por un mensaje que nunca llega. Esto podría deberse a varias razones. Por ejemplo, podría haber un error lógico en nuestro programa, o el proceso que nos iba a enviar un mensaje podría fallar antes de enviar el mensaje. Para evitar este problema, podemos agregar un tiempo de espera a la declaración de recepción. Esto establece un tiempo máximo que esperará el proceso para recibir un mensaje.

A continuación se muestra la sintaxis del mensaje de recepción con un tiempo de espera especificado

Sintaxis

receive 
Pattern1 [when Guard1] -> 
Expressions1; 

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

El ejemplo más simple es crear una función durmiente como se muestra en el siguiente programa.

Ejemplo

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

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

El código anterior dormirá durante 1000 Ms antes de salir.

Recepción selectiva

Cada proceso en Erlang tiene un buzón asociado. Cuando envía un mensaje al proceso, el mensaje se coloca en el buzón. La única vez que se examina este buzón es cuando su programa evalúa una declaración de recepción.

A continuación se muestra la sintaxis general de la instrucción de recepción selectiva.

Sintaxis

receive 
Pattern1 [when Guard1] ->
Expressions1; 

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

Así es como funciona la declaración de recepción anterior:

  • Cuando ingresamos una instrucción de recepción, iniciamos un temporizador (pero solo si hay una sección posterior en la expresión).

  • Tome el primer mensaje del buzón e intente compararlo con Pattern1, Pattern2, etc. Si la coincidencia tiene éxito, el mensaje se elimina del buzón y se evalúan las expresiones que siguen el patrón.

  • Si ninguno de los patrones de la declaración de recepción coincide con el primer mensaje del buzón, el primer mensaje se elimina del buzón y se coloca en una "cola de guardado". Luego se intenta el segundo mensaje en el buzón. Este procedimiento se repite hasta que se encuentra un mensaje coincidente o hasta que se han examinado todos los mensajes del buzón.

  • Si ninguno de los mensajes del buzón coincide, el proceso se suspende y se reprogramará para su ejecución la próxima vez que se coloque un nuevo mensaje en el buzón. Tenga en cuenta que cuando llega un mensaje nuevo, los mensajes de la cola de guardado no se vuelven a emparejar; solo el nuevo mensaje coincide.

  • Tan pronto como se haya hecho coincidir un mensaje, todos los mensajes que se colocaron en la cola de guardado se vuelven a ingresar al buzón en el orden en que llegaron al proceso. Si se configuró un temporizador, se borra.

  • Si el temporizador transcurre cuando estamos esperando un mensaje, evalúe las expresiones ExpressionsTimeout y vuelva a colocar los mensajes guardados en el buzón en el orden en que llegaron al proceso.