aprendiendo ( Erlang ).

miércoles, 1 de junio de 2011

Introducción a procesos

| 6 comentarios |

Como ya hemos visto con anterioridad Erlang implementa el paradigma de orientación a procesos. En mi humilde opinión uno de los puntos fuertes del lenguaje.
Un punto a destacar es que los procesos son del propio lenguaje No procesos del sistema.
En Erlang los procesos:
  • Son rápidos de crear y destruir.
  • El envío de mensajes entre procesos es muy rápido.
  • Es fácil mantener un gran número de procesos.
  • Son ligeros.
  • Son independientes y no comparten memoria.
  • Sólo un proceso tratará un mensaje pasado, en ningún caso pasará por otro proceso.
Si has trabajado antes con procesos sabrás las complicaciones que pueden existir. Tal ves, conozcas las mil y una historias de problemas de compartición de memoria, las dificultades de trazas, los semáforos y demás historias, que te habrán dejado sin sueño más de una vez. Pues bien, en Erlang muchos de estos problemas no los tendrás ya que esta pensado para minimizar estas y otras problemáticas.

Primitivas básicas

  • Pid = spawn(Fun)

    Crea un nuevo proceso concurrente donde Fun es la función concurrente. El nuevo proceso puede ser evaluado en paralelo. La función spawn retorna el Pid del proceso que podremos utilizar para el envío de mensajes al proceso. El Pid del proceso actual se obtiene con la función self/0
  • Pid ! Message

    Esta construcción nos permite el envío de mensajes al proceso identificado por el Pid. El mensaje es enviado asíncronamente, por lo que, no tenemos que esperar a que el proceso nos responda. Cuando realizamos Pid ! Mensaje Erlang nos responde siempre con Mensaje. Se puede enviar el mismo mensaje a distintos procesos, por ejemplos, Pid1 ! Pid2 ! Pid3 ! Mensaje se envía el mensaje a todos los procesos en el orden establecido.
  • receive ... end

    La construcción receive se encarga de recepcionar y tratar el mensaje enviado al proceso.
    La sintaxis es:
    receive
      Patron1 [when Guarda1] ->
         secuencia de comandos1;
      ...
      PatronN [when GuardaN] ->
         secuencia de comandosN
    end
    
    El comportamiento de receive es similar al de una sentencia case. Cuando un mensaje es recibido por un proceso el sistema busca coincidencias con el patrón 1 (condicionado a la guarda 1) y si tiene éxito ejecuta la secuencias de comandos correspondientes. En caso contrario, continúa con el patrón 2 y así sucesivamente.

Ejemplo de uso

Aunque ya implementamos un hola mundo, veamos otro ejemplo que nos ilustre un poco más el uso de procesos.
-module(calculadora).
-export([start/0]).

start () ->
    spawn(fun calcular/0).

calcular() ->
    receive
        {suma, X, Y} ->
            io:format ("~p + ~p = ~p ~n", [X, Y, X+Y]),
            calcular();
        {resta, X, Y} ->
            io:format ("~p - ~p = ~p ~n", [X, Y, X-Y]),
            calcular();
        {multiplica, X, Y} ->
            io:format ("~p * ~p = ~p ~n", [X, Y, X*Y]),
            calcular();
        {divide, X, Y} when Y > 0 ->
            io:format ("~p / ~p = ~p ~n", [X, Y, X/Y]),
            calcular();
        terminar ->
            void;
        Otro ->
            io:format("Error: comando <~p> incorrecto~n", [Otro]), 
            calcular()
    end.
Hemos implementado un pequeño proceso que nos permite realizar pequeños cálculos. En definitiva una calculadora que suma, resta, multiplica y divide.
La función start/0 crea un nuevo proceso con la función calcular/0. Esta función calcular/0 tiene como misión realizar los cálculos que se le pasan por mensajes. Los patrones elegidos son tuplas en el cual el primer elemento es la operador a realiza y el resto los valores a operar. Siempre después de operar llamamos a la función proceso (calcular/0) para permitir que se siga realizando cálculos.
La operación divide esta condicionada con una guarda para que no se puedan dar divisiones por cero.
El patrón terminar da por concluido la recepción de mensajes. Dejando de operar nuestra calculadoras.
El patrón Otro puesto al final nos vale para recoger todos aquellos mensajes que no están permitidos a modo de error.
Veamos el uso de la calculadora.
1> c(calculadora).
{ok,calculadora}
2> Calc = calculadora:start().
<0.42.0>
3> Calc!{suma, 2, 4}.
2 + 4 = 6 
{suma,2,4}
4> Calc!{resta, 6, 4}. 
6 - 4 = 2 
{resta,6,4}
5> Calc!{multiplica, 6, 4}.
6 * 4 = 24 
{multiplica,6,4}
6> Calc!{divide, 6, 4}.    
6 / 4 = 1.5 
{divide,6,4} 
7> Calc!{divide, 6, 0}. 
Error: comando <{divide,6,0}> incorrecto
{divide,6,0}
8> Calc!terminar.      
terminar

Publicar un comentario en la entrada

6 comentarios:

  1. Anónimo dijo...

    Tengo que rendir un final, y al profesor se le ocurrio enseñarnos este lenguaje, la verdad no me gusta nada, no entiendo mucho el paradigma funcional, pero bueno voy a ir probando tus ejemplos y veo como la piloteo, muchas gracias, son de gran ayuda tus post!!

  2. Verdi dijo...

    Espero realmente que te sea de ayuda. Es para ello, para lo que nació este blog. Para yo aprender y para que sirvan mis experiencias a otros.

    A mí personalmente, cada día que pasa me gusta más el lenguaje le estoy encontrando el gustillo.

    Si estas interesado en algún tema o problema concreto que no este posteado puedes sugerírmelo.

    Gracias

  3. Anónimo dijo...

    Genio! segui asi, tus ejemplos me sirvieron de mucho.

  4. Emanuelpeg dijo...

    Muy buen post gracias por compartir!

  5. Andres Ramirez dijo...

    muy pero muy buen post.... felicitaciones siga asi, me han ensenado mucho sobre erlang :)

  6. Verdi dijo...

    Un placer Andrés. Ese es el sentido del blog.

    Gracias

 
Licencia Creative Commons
Aprendiendo Erlang por Verdi se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported.