Erlang - Concorrenza
La programmazione simultanea in Erlang deve avere i seguenti principi o processi di base.
L'elenco include i seguenti principi:
piD = spawn (divertente)
Crea un nuovo processo simultaneo che valuta il divertimento. Il nuovo processo viene eseguito in parallelo con il chiamante. Un esempio è il seguente:
Esempio
-module(helloworld).
-export([start/0]).
start() ->
spawn(fun() -> server("Hello") end).
server(Message) ->
io:fwrite("~p",[Message]).
L'output del programma di cui sopra è:
Produzione
“Hello”
Pid! Messaggio
Invia un messaggio al processo con l'identificatore Pid. L'invio del messaggio è asincrono. Il mittente non attende ma continua con quello che stava facendo.‘!’ è chiamato operatore di invio.
Un esempio è il seguente:
Esempio
-module(helloworld).
-export([start/0]).
start() ->
Pid = spawn(fun() -> server("Hello") end),
Pid ! {hello}.
server(Message) ->
io:fwrite("~p",[Message]).
Ricevi ... fine
Riceve un messaggio che è stato inviato a un processo. Ha la seguente sintassi:
Sintassi
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
End
Quando un messaggio arriva al processo, il sistema cerca di confrontarlo con Pattern1 (con possibile guardia Guard1); se ha successo, valuta Expressions1. Se il primo pattern non corrisponde, prova a Pattern2 e così via. Se nessuno dei modelli corrisponde, il messaggio viene salvato per l'elaborazione successiva e il processo attende il messaggio successivo.
Un esempio dell'intero processo con tutti e 3 i comandi è mostrato nel seguente programma.
Esempio
-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}.
Le seguenti cose devono essere annotate sul programma di cui sopra:
La funzione loop ha il ciclo di fine ricezione. Quindi, quando un messaggio viene inviato, verrà elaborato dal ciclo finale di ricezione.
Viene generato un nuovo processo che va alla funzione loop.
Il messaggio viene inviato al processo generato tramite Pid! comando messaggio.
L'output del programma di cui sopra è:
Produzione
Area of the Rectangle is 60
Numero massimo di processi
In concomitanza è importante determinare il numero massimo di processi consentiti su un sistema. Dovresti quindi essere in grado di capire quanti processi possono essere eseguiti contemporaneamente su un sistema.
Vediamo un esempio di come possiamo determinare qual è il numero massimo di processi che possono essere eseguiti su 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).
Su qualsiasi macchina che abbia una buona potenza di elaborazione, passeranno entrambe le funzioni max. Di seguito è riportato un esempio di output del programma precedente.
Maximum allowed processes:262144
Process spawn time=47.0 (16.0) microseconds
Maximum allowed processes:262144
Process spawn time=12.81 (10.15) microseconds
Ricevi con un timeout
A volte una dichiarazione di ricezione potrebbe attendere per sempre un messaggio che non arriva mai. Ciò potrebbe essere dovuto a una serie di ragioni. Ad esempio, potrebbe esserci un errore logico nel nostro programma o il processo che stava per inviarci un messaggio potrebbe essere andato in crash prima di inviare il messaggio. Per evitare questo problema, possiamo aggiungere un timeout all'istruzione di ricezione. Questo imposta un tempo massimo che il processo attenderà per ricevere un messaggio.
Di seguito è riportata la sintassi del messaggio di ricezione con un timeout specificato
Sintassi
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
after Time ->
Expressions
end
L'esempio più semplice è creare una funzione dormiente come mostrato nel seguente programma.
Esempio
-module(helloworld).
-export([sleep/1,start/0]).
sleep(T) ->
receive
after T ->
true
end.
start()->
sleep(1000).
Il codice precedente dormirà per 1000 Ms prima di uscire effettivamente.
Ricezione selettiva
Ogni processo in Erlang ha una casella di posta associata. Quando si invia un messaggio al processo, il messaggio viene inserito nella cassetta postale. L'unica volta che questa casella di posta viene esaminata è quando il programma valuta una dichiarazione di ricezione.
Di seguito è riportata la sintassi generale dell'istruzione di ricezione selettiva.
Sintassi
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard1] ->
Expressions1;
...
after
Time ->
ExpressionTimeout
end
Ecco come funziona la dichiarazione di ricezione sopra:
Quando si inserisce un'istruzione di ricezione, si avvia un timer (ma solo se nell'espressione è presente una sezione after).
Prendi il primo messaggio nella casella di posta e prova a confrontarlo con Pattern1, Pattern2 e così via. Se la corrispondenza ha esito positivo, il messaggio viene rimosso dalla cassetta postale e vengono valutate le espressioni che seguono il modello.
Se nessuno dei modelli nell'istruzione di ricezione corrisponde al primo messaggio nella cassetta postale, il primo messaggio viene rimosso dalla cassetta postale e inserito in una "coda di salvataggio". Viene quindi provato il secondo messaggio nella cassetta postale. Questa procedura viene ripetuta fino a quando non viene trovato un messaggio corrispondente o fino a quando tutti i messaggi nella casella di posta non sono stati esaminati.
Se nessuno dei messaggi nella cassetta postale corrisponde, il processo viene sospeso e verrà ripianificato per l'esecuzione la prossima volta che un nuovo messaggio viene inserito nella cassetta postale. Notare che quando arriva un nuovo messaggio, i messaggi nella coda di salvataggio non vengono rimappati; solo il nuovo messaggio è abbinato.
Non appena un messaggio è stato trovato, tutti i messaggi che sono stati inseriti nella coda di salvataggio vengono reinseriti nella casella di posta nell'ordine in cui sono arrivati al processo. Se è stato impostato un timer, viene cancellato.
Se il timer scade quando siamo in attesa di un messaggio, valuta le espressioni ExpressionsTimeout e rimetti i messaggi salvati nella casella di posta nell'ordine in cui sono arrivati al processo.