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