Top

Prozess

Die Parallelität von Erlnag ist der Prozess. Ein Prozess ist unabhängig von anderen Prozessen.
Die Erlang Prozesse sind sehr klein und starten sowie stoppen sehr schnell. Die VM kann sehr viele Prozesse verarbeiten.
Mit der Funktion spawn wird ein neuer Prozess erzeugt, die übergebene Funktion muss eine exportierte sein, dabei wird ein Process Identifikator zurück gegeben.

1> SimplePro = fun() -> io:format("Process ~w~n", [self()]) end.
#Fun<erl_eval.45.97283095>
2> Pid = spawn(SimplePro).
Process <0.91.0>
<0.91.0>

Der Process Identifikator reicht aus um mit dem Prozess zu interagieren.
Erlang bietet aber auch die Möglichkeit den Prozess mit einem Namen zu registrieren.

3> register(myprocess, spawn(SimplePro)).
Process <0.96.0>
true

Alle Prozesse ausgeben:

registered().
regs().
i().

Erlang bietet zusätzlich eine Applikation die Observer heisst, mit dieser hat man eine gute Einsicht in die laufenden Prozesse.

$ erl -run observer

Messaging

Prozesse können Nachrichten senden und empfangen, dazu wird der Operator ! verwendet.
Jeder Prozess hat eine Mailbox, wo die Nachrichten angehängt werden und diese können mit receive abgeholt werden.

-module(simple_process).
-export([simple_process/0, run/0]).

simple_process() ->
    receive
        Request ->
            io:format("Received: ~s~n", [Request]),
            theprocess()
    end.

run() ->
    Pid = spawn(?MODULE, simple_process, []),
    Pid ! msg.

Beim kompilieren und aufrufen bekommt man diese Ausgabe.

1> c(simple_process).
{ok,simple_process}
2> simple_process:run().
Received: msg

Wenn man zwei Prozesse miteinander kommunizieren lassen will, könnte dies so aussehen.

-module(chatting_process).
-export([run/0, pro_a/0, pro_b/0]).

pro_a() ->
    receive
        {Pid_B, talk} ->
            io:format("A (~p) ~p ~p~n", [self(), talk, Pid_B]),
            timer:sleep(500),
            Pid_B ! {self(), talk},
            pro_a()
    end.

pro_b() ->
    receive
        {Pid_A, talk} ->            
            io:format("B (~p) ~p ~p~n", [self(), talk, Pid_A]),
            timer:sleep(500),
            Pid_A ! {self(), talk},            
            pro_b()
    end.

run() ->
    Pid_A = spawn(?MODULE, pro_a, []),
    Pid_B = spawn(?MODULE, pro_b, []),
    Pid_B ! {Pid_A, talk}.

Beim kompilieren und aufrufen senden die beiden Prozesse, wobei B anfängt an A zu senden und dann immer endlos hin und her.

1> c(chatting_process).
{ok,chatting_process}
2> chatting_process:run().
B (<0.88.0>) talk <0.87.0>
A (<0.87.0>) talk <0.88.0>
B (<0.88.0>) talk <0.87.0>
...

Monitoring

Prozesse können sich überwachen, diese Verbindung ist unidirektional.
Man nutzt dazu erlang:monitor oder spawn_monitor um eine Reference zu erhalten.

-module(monitoring_process).
-export([sub_pro/0, run/0]).

sub_pro() ->
    I = rand:uniform(2000),
    io:format("Subprocess ~p will die in ~p msec.~n", [self(), I]),
    receive
    after I ->
        ok
    end.

main_pro(0) ->
    io:format("We're done...~n");

main_pro(N) ->
    io:format("Main process starts ~p times...~n", [N]),
    Pid1 = spawn(?MODULE, sub_pro, []),
    Ref1 = erlang:monitor(process, Pid1),
    {Pid2, Ref2} = spawn_monitor(?MODULE, sub_pro, []),
    io:format("~p monitors ~p ~p ...~n", [self(), Pid1, Ref1]),    
    io:format("~p monitors ~p ~p ...~n", [self(), Pid2, Ref2]),
    receive
        {'DOWN', Ref1, process, Pid1, Reason} ->
            io:format("~p have a first subprocess (~p) died. Reason: ~p~n", [self(), Pid1, Reason]),
            main_pro(N - 1);
        {'DOWN', Ref2, process, Pid2, Reason} ->
            io:format("~p have a second subprocess (~p) died. Reason: ~p~n", [self(), Pid2, Reason]),
            main_pro(N - 1)
    end.

run() ->
    main_pro(2).

Beim kompilieren und aufrufen bekommt man diese Ausgabe mit unterschiedlichen Werten.

1> c(monitoring_process).
{ok,monitoring_process}
2> monitoring_process:run().
Main process starts 2 times...
<0.80.0> monitors <0.97.0> #Ref<0.3457018843.721420292.115364> ...
Subprocess <0.97.0> will die in 1036 msec.
Subprocess <0.98.0> will die in 741 msec.
<0.80.0> monitors <0.98.0> #Ref<0.3457018843.721420292.115365> ...
<0.80.0> have a second subprocess (<0.98.0>) died. Reason: normal
Main process starts 1 times...
<0.80.0> monitors <0.99.0> #Ref<0.3457018843.721420292.115372> ...
Subprocess <0.99.0> will die in 1326 msec.
Subprocess <0.100.0> will die in 426 msec.
<0.80.0> monitors <0.100.0> #Ref<0.3457018843.721420292.115373> ...
<0.80.0> have a second subprocess (<0.100.0>) died. Reason: normal
We're done...
ok

Linking

Prozesse können miteinander verlinkt werden, diese Verbindung ist bidirektional.
Die Funktion spawn wird mit der Funktion link kombiniert oder man nutzt spawn_link.

-module(linking_process).
-export([sub_pro/0, run/0]).

sub_pro() ->
    I = rand:uniform(2000),
    io:format("Subprocess ~p will die in ~p msec.~n", [self(), I]),
    receive
    after I ->
        ok
    end.

main_pro(0) ->
    io:format("We're done...~n");

main_pro(N) ->
    io:format("Main process starts ~p times...~n", [N]),
    Pid1 = spawn(?MODULE, sub_pro, []),
    link(Pid1),
    Pid2 = spawn_link(?MODULE, sub_pro, []),
    process_flag(trap_exit, true),
    receive
        {'EXIT', Pid1, Reason} ->
            %timer:sleep(10),
            io:format("~p have a first subprocess (~p) died. Reason: ~p~n", [self(), Pid1, Reason]),
            %timer:sleep(1000),
            main_pro(N - 1);
        {'EXIT', Pid2, Reason} ->
            %timer:sleep(10),
            io:format("~p have a second subprocess (~p) died. Reason: ~p~n", [self(), Pid2, Reason]),
            %timer:sleep(1000),
            main_pro(N - 1)
    end.

run() ->
    main_pro(2).

Beim kompilieren und aufrufen bekommt man diese Ausgabe mit unterschiedlichen Werten.

1> c(linking_process).
{ok,linking_process}
2> linking_process:run().
Main process starts 2 times...
Subprocess <0.122.0> will die in 1399 msec.
Subprocess <0.123.0> will die in 724 msec.
<0.80.0> have a second subprocess (<0.123.0>) died. Reason: normal
Main process starts 1 times...
Subprocess <0.124.0> will die in 1364 msec.
Subprocess <0.125.0> will die in 1960 msec.
<0.80.0> have a first subprocess (<0.124.0>) died. Reason: normal
We're done...
ok