Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Erlang messaging


May 13, 2021 Erlang


Table of contents


Erlang messaging

In the following example, two processes are created that send multiple messages to each other.

-module(tut15).

-export([start/0, ping/2, pong/0]).

ping(0, Pong_PID) ->
    Pong_PID ! finished,
    io:format("ping finished~n", []);

ping(N, Pong_PID) ->
    Pong_PID ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping(N - 1, Pong_PID).

pong() ->
    receive
        finished ->
            io:format("Pong finished~n", []);
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.

start() ->
    Pong_PID = spawn(tut15, pong, []),
    spawn(tut15, ping, [3, Pong_PID]).
1> c(tut15).
{ok,tut15}
2> tut15: start().
<0.36.0>
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
ping finished
Pong finished

The start function first creates a process, which we call "pong":

Pong_PID = spawn(tut15, pong, [])

This process executes the tut15:pong function. P ong_PID is the process identifier of the "pong" process. N ext, the start function creates another process, ping:

spawn(tut15,ping,[3,Pong_PID]),

This process executes:

tut15:ping(3, Pong_PID)

The return value of the start function is the <0.36.0>

The "pong" process accomplishes the following:

receive
    finished ->
        io:format("Pong finished~n", []);
    {ping, Ping_PID} ->
        io:format("Pong received ping~n", []),
        Ping_PID ! pong,
        pong()
end.

The receive keyword is used by processes to receive messages sent from other processes. I ts usage syntax is as follows:

receive
   pattern1 ->
       actions1;
   pattern2 ->
       actions2;
   ....
   patternN
       actionsN
end.

Note that the last action before end does not "; "

Messages between Erlang processes can be any simple Erlang item. F or example, it can be lists, yuans, integers, atoms, process identities, and so on.

Each process has a separate message receiving queue. T he newly received message is placed at the end of the receive queue. W hen the process executes receive, the first message in the message matches the first module after receive. I f the match is successful, the message is removed from the message queue and the code that follows the pattern is executed.

However, if the first pattern match fails, the second match is tested. I f the second match is successful, the message is removed from the message queue and the code after the second match is executed. I f the second match also fails, the third one is matched, and so on, until all patterns match the end. I f all matches fail, leave the first message in the message queue and repeat the previous procedure with the second message. W hen the second message match is successful, the program after the match is successful is executed and the message is removed from the message queue (the first message remains in the message queue with the remaining messages). I f the second message also fails to match, try the third message, and so on, until all messages in the message queue have been tried. I f all messages are processed at an end (match failed or the match was successfully removed), the process blocks and waits for the new message to arrive. T he above process will be repeated all the time.

The Erlang implementation is very "smart" and minimizes the number of times each message of receive matches the pattern to the test.

Let's go back to the ping pong sample program.

"Pong" waits for the message to be received. I f you receive the atomic value finished, "Pong" outputs "Pong finished" and then ends the process. I f you receive a message in the form of:

{ping, Ping_PID}

outputs "Pong received ping" and sends an atomic message pong to the process "ping":

Ping_PID ! pong

Notice here how the message is sent using the "!" operator. T he syntax of the "!" operator looks like this:

Pid ! Message

This means that messages (any Erlang data) are sent to the message queue of a process with a process identifier, Pid.

After sending the message pong to the process "ping", the "pong" process calls the pong function again, which causes the receive to return again and wait for the next message to arrive.

Let's take a look at the process "ping" and recall that it started below:

tut15:ping(3, Pong_PID)

You can look at the ping/2 function, because the value of the first argument is 3 instead of 0, so the second clause of the ping/2 function is executed (the head of ping(0,Pong_PID) the head of the second clause is ping ping(N,Pong_PID) so N is 3.

The second clause sends a message to the "pong" process:

Pong_PID ! {ping, self()},

The self() function returns the process identifier of the current process (the process that executed self(), here is the process identifier of the "ping" process. ( Recall the code for "pong", the process identifier value is stored in the variable Ping_PID)

After sending the message, "Ping" waits for the reply message "pong":

receive
    pong ->
        io:format("Ping received pong~n", [])
end,

When you receive a reply message, output "Ping received pong". T he ping function is then called again:

ping(N - 1, Pong_PID)

N-1 gradually reduces the first argument to 0. W hen its value changes to 0, the first clause of the ping/2 function is executed.

ping(0, Pong_PID) ->
    Pong_PID !  finished,
    io:format("ping finished~n", []);

At this point, the atomic value finished is sent to the "pong" process, which causes the process to end, and the "ping finished" output is output. T he Ping process then ends.