SoFunction
Updated on 2025-04-08

Introduction to Erlang concurrent programming

process in Erlang - the process is lightweight and there is no sharing between processes. After checking a lot of information, no one seemed to explain what the concept of lightweight processes is. We continue to search. . . Without talking about gossip, enter the world of concurrent programming. This article is considered a study notes, and it can also be said to be a brief translation of the fifth picture of "Concurrent Programming in ERLANG".

1. Process creation

A process is a self-contained, separated computing unit and runs concurrently in the system with other processes. There is no inheritance system between processes. Of course, application developers can design such an inheritance system.
The creation of the process uses the following syntax:

Copy the codeThe code is as follows:

Pid = spawn(Module, FunctionName, ArgumentList)

spawn takes three parameters: module name, function name, and parameter list, and returns an identifier (Pid) representing the created process.
If executed in a known process Pid1:
Copy the codeThe code is as follows:

Pid2 = spawn(Mod, Func, Args)

Then, Pid2 can only be visible to Pid1, and the security of the Erlang system is built on the basis of restricting process expansion.

2. Inter-process communication

Communication between Erlang processes can only be achieved by sending messages, and the sending of messages uses the! symbol:

Copy the codeThe code is as follows:

Pid ! Message

where Pid is the process tag that accepts messages, and Message is the message. The recipient and message can be any valid Erlang structure as long as their result returns a process tag and message.
The acceptance of messages is to use the receive keyword, and the syntax is as follows:
 
Copy the codeThe code is as follows:

receive
      Message1 [when Guard1] ->
          Actions1 ;
      Message2 [when Guard2] ->
          Actions2 ;

end

Each Erlang process has a "mailbox". All messages sent to the process are stored in the "mailbox" in the order of arrival. The messages Message1 and Message2 shown above match the messages in the "mailbox" and the constraint (Guard) passes, then the corresponding ActionN will be executed, and the receive returns the result of the last execution statement of ActionN. Erlang is selective for matching messages in the "mailbox". Only matched messages will be triggered by the corresponding action, while non-matched messages will remain in the "mailbox". This mechanism ensures that no messages will block the arrival of other messages.
The order in which messages arrive does not determine the priority of the messages. The process will take turns checking the messages in the "emailbox" to try to match. The priority level of the message will be discussed later.

How to accept messages from specific processes? The answer is very simple. The sender is also attached to the message. The receiver decides whether to accept it through pattern matching, such as:
 

Copy the codeThe code is as follows:

Pid ! {self(),abc}

Send a message {self(),abc} to the process Pid, ​​and use the self process to get the sender to send as a message. Then the receiver:
Copy the codeThe code is as follows:

receive
  {Pid1,Msg} ->

end


Only messages sent by the Pid1 process will be accepted through pattern matching.

3. Some examples
Only explain the process examples of counting in the following book, I added a simple comment:

Copy the codeThe code is as follows:

-module(counter).
-compile(export_all).
% start(), returns a new process, the process executes the function loop
start()->spawn(counter, loop,[0]).
% Call this operation to increment the count
increment(Counter)->
    Counter!increament.
% Returns the current count value
value(Counter)->
    Counter!{self(),value},
    receive
        {Counter,Value}->
% is returned to the caller
            Value
        end.
%Stop counting
 stop(Counter)->
     Counter!{self(),stop}.
 loop(Val)->
     receive
% accepts different messages and decides to return the result
         increament->
             loop(Val+1);
         {From,value}->
             From!{self(),Val},
             loop(Val);
         stop->
             true;
% is not the above three messages, so continue to wait
         Other->
             loop(Val)
      end.  


Call method:

Copy the codeThe code is as follows:

1> Counter1=counter:start().
<0.30.0>
2> counter:value(Counter1).
0
3> counter:increment(Counter1).
increament
4> counter:value(Counter1).
1

Process-based messaging mechanisms can easily implement finite state machines (FSMs), states are represented by functions, and events are messages. Not to expand it in detail

4. Timeout setting

The receive syntax in Erlang can add an extra option: timeout, similar to:

Copy the codeThe code is as follows:

receive
   Message1 [when Guard1] ->
     Actions1 ;
   Message2 [when Guard2] ->
     Actions2 ;
  
   after
      TimeOutExpr ->
         ActionsT
end

The TimeOutExpr expression after after returns an integer time (millisecond level), and the accuracy of time depends on Erlang's implementation in the operating system or hardware. If no message is selected within time milliseconds, the timeout setting will take effect, that is, ActionT will be executed. Time has two special values:

1) infinity (infinity), infinity is an atom, and the timeout setting specified will never be executed.
2) 0. If the timeout is set to 0, it means that the timeout setting will be executed immediately, but the system will first try the message in the current "mailbox".

There are several common applications for timeouts, such as how many milliseconds of the current process are suspended:

Copy the codeThe code is as follows:

sleep(Time) ->
  receive
    after Time ->
    true
end.

For example, clear the process's "emailbox" and discard all messages in the "emailbox":
 

Copy the codeThe code is as follows:
 
flush_buffer() ->
  receive
    AnyMessage ->
      flush_buffer()
  after 0 ->
    true
end.

Hang the current process forever:
Copy the codeThe code is as follows:

  suspend() ->
    receive
    after
        infinity ->
            true
    end.

Timeout can also be applied to implementing timers. For example, in the following example, create a process that will send a message to itself after setting the time:
Copy the codeThe code is as follows:

-module(timer).
-export([timeout/2,cancel/1,timer/3]).
timeout(Time, Alarm) ->
   spawn(timer, timer, [self(),Time,Alarm]).
cancel(Timer) ->
   Timer ! {self(),cancel}.
timer(Pid, Time, Alarm) ->
   receive
    {Pid,cancel} ->
       true
   after Time ->
       Pid ! Alarm
end.

5. Registration process
In order to send messages to a process, we need to know the process's Pid, ​​but in some cases: there are many global servers in a large system, or the process's Pid needs to be hidden for security reasons. In order to achieve the purpose of sending messages to a process that does not know Pid, ​​we provide a way to register a process and register names for the processes, which must be atoms.
Basic call form:

Copy the codeThe code is as follows:

register(Name, Pid)

Connect Name to Process Pid
Copy the codeThe code is as follows:

unregister(Name)

Cancel the correspondence between Name and the corresponding process.
Copy the codeThe code is as follows:

whereis(Name)

Returns the Pid of the process associated with Name. If no process is associated with it, it returns atom:undefined
Copy the codeThe code is as follows:

registered()

Returns the name list of currently registered processes

6. Process priority

To set the priority of the process, BIFs can be used:

Copy the codeThe code is as follows:

process_flag(priority, Pri)

Pri can be normal or low, and the default is normal
Processes with high priority will execute a little more with relatively low priority.

7. Process group
All ERLANG processes have a Pid associated with a shared group called Group Leader. When a new process is created, it will be added to the same process group. The Group Leader of the original system process is itself, so it is also the Group Leader of all created processes and child processes. This means that Erlang's process is organized into a Tree, and the root node is the first process to be created. The following BIFs are used to manipulate process groups:

group_leader()
Returns the Pid of the Group Leader that executes the process

group_leader(Leader, Pid)
Set the Group Leader of the process Pid as the Leader of the process

The process model is easy to build Client-Server model, There is a section in the book that specifically discusses this point, focusing on the design of interfaces and isolation issues at the abstract level, and will not be translated.