English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Erlang Concurrency

Erlang concurrent programming needs to follow the following basic principles or processes.

The list includes the following principles:

piD = spawn(Fun)

Create a new concurrent process to evaluate Fun. The new process runs in parallel with the caller. An example is as follows-

Example

-module(helloworld). 
-export([start/0]). 
start() ->
   spawn(fun()) -server("Hello") end). 
server(Message) ->
   io:fwrite("~p", [Message]).

The output of the above program is-

Output

"Hello"

Pid ! Message

Messages are sent to a process using the identifier Pid. The message sending is asynchronous. The sender does not wait but continues with what it is doing. The '!' symbol is called the send operator.

An example is as follows-

Example

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

Receive…end

Receive messages sent to the process. It has the following syntax-

Syntax

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

When a message arrives at the process, the system will try to match it with Pattern1matching (which may have Guard 1()). If successful, then for Expressions1evaluation. If the first pattern does not match, try using Pattern2and so on. If no pattern matches, save the message for future processing, and then the process waits for the next message.

The following program shows the use of all3an example of the entire process of issuing the command.

Example

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

The following points should be noted about the above program:

  • The loop function has a receiving end loop. Therefore, when a message is sent, it will be processed by the receiving end loop.

  • Generate a new process that will go to the loop function.

  • Messages are sent to the generated process using the Pid! message command.

The output of the above program is-

Output

Area of the Rectangle is 60

Maximum number of processes

Concurrently, it is important to determine the maximum number of processes allowed on the system. Then, you should be able to understand how many processes can be executed simultaneously on the system.

Let's look at an example of how to determine the maximum number of processes that can be executed on the system.

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

The two maximum functions above will pass on any machine with good processing capabilities. Below is an example output of the above program.

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

Timeout Receive

Sometimes, the receive statement may wait indefinitely for a message that will never appear. There may be many reasons for this. For example, there may be logical errors in our program, or the process that is supposed to send us a message may have crashed before sending the message. To avoid this problem, we can add a timeout to the receive statement. This will set the maximum time the process waits to receive a message.

The syntax for receiving messages with a timeout is specified below

Syntax

receive 
Pattern1 [when Guard1] -> 
Expressions1; 
Pattern2 [when Guard2] ->
Expressions2; 
... 
after Time -> 
Expressions 
end

The simplest example is to create a sleeper function, as shown in the following program.

Example

-module(helloworld). 
-export([sleep/1,start/0]). 
sleep(T) ->
   receive 
   after T -> 
      true 
   end. 
   
start()->
   sleep(1000).

The above code will sleep before actually exiting1000 milliseconds.

Selective Receive

Each process in Erlang has an associated mailbox. When you send a message to the process, the message is placed in the mailbox. The mailbox is checked only when the program evaluates the receiving statement.

The general syntax of the 'selective receive' statement is as follows.

Syntax

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

This is how the above receiving statement works-

  • When we input a receive statement, we start a timer (but only if there is an after clause in the expression).

  • with the first email in the mailbox, and try to match it with Pattern1, Pattern2Wait for a match. If the match is successful, the email will be removed from the mailbox and the expression following the pattern will be evaluated.

  • If none of the patterns in the receive statement match the first message in the mailbox, the first message in the mailbox will be deleted and placed into the 'save queue'. Then try the second message in the mailbox. Repeat this process until a matching message is found or all messages in the mailbox have been checked.

  • If all messages in the mailbox do not match, the process will be suspended and rescheduled for execution when a new message is placed in the mailbox. Note that when a new message is received, messages in the save queue will not be rematched; only the new message will be matched.

  • Once a message is matched, all messages placed in the save queue will be re-entered into the mailbox in the order of the arrival process. If a timer is set, it will be cleared.

  • If the timer has expired while waiting for a message, evaluate the expression ExpressionsTimeout and place all saved messages back in the mailbox in the order of the arrival process.