Concurrent Erlang
I’m sure you’ve met processes before, but only in the context of operating systems. In Erlang, processes belong to the programming language and not the operating system. This means that Erlang processes will have the same logical behavior on any operating system, so we can write portable concurrent code that can run on any operating system that supports Erlang.
In Erlang:
- Creating and destroying processes is very fast.
- Sending messages between processes is very fast.
- Processes behave the same way on all operating systems.
- We can have very large numbers of processes.
- Processes share no memory and are completely independent.
- The only way for processes to interact is through message passing.
Processes
In Erlang, creation of a parallel process is achieved by evaluating the spawn
primitive. This primitive creates a concurrent process and returns a process
identifier (PID) that can used to interact with the newly created process:
Pid = spawn(ModName, FuncName, [Arg1, Arg2, ..., ArgN]).
- This BIF never fails
- A process can either terminate:
- Abormally when run-time errors occur
- Normally when there is no more code to execute
Modeling Concurrency
The syntax Pid ! Msg
means "send the message Msg
to the process Pid
", where Msg
is from any valid Erlang data type. Also, Pid1 ! Pid2 ! ... ! Msg
means send the
message Msg to all the processes Pid1
, Pid2
, and so on. The receive ... end
construct is used for a process to read a message that has been sent:
receive Pattern1 [when Guard1] -> Expression1; Pattern2 [when Guard2] -> Expression2; ... end
- Sending a message will never fail.
- A message sent to non-existing processes are throw away.
- Received messages are store in a process' mailbox.
- Mailboxes are scanned sequentially.
If a message fails to be pattern matched, it's saved in a queue for later processing and the process waits for the next message. It is possible to update a process with a new version of the code that retrieves those messages.
The erlang shell (
erl
) is itself a process, you can test its message-passing functionalities by using theself
keyword:1> self() ! hello. hello 2> receive X -> X end. hello
- Messages can be matched and selectivelly retrieved.
We can force an order (emulating a priority queue) by using multiple receives:
receive {foo, _} -> (...) end, (...) receive {bar, _} -> (...) end,
this way the message
{foo, ...}
is received before the message{bar, ...}
.
Registered Processes
BIF | Description |
---|---|
register(Name, Pid) |
Associates the name Name , an atom, with the process Pid . |
registered/0 |
Returns a list of names that have been registered using register/2 . |
whereis(Name) |
Returns the pid registered under Name , or undefined if the name is not registered. |
registered() |
Return a list of all registered processes in the system. |
- Sending messages to a non-existing registered process causes the calling
process to terminate with a
badarg
error.
Timeouts
- If the message
Msg
is received within theTimeOut
,expr01
will be executed. Otherwise,expr02
will be executed. TimeOut
is an integer denoting the time in miliseconds or the Atominfinity
.
receive Msg -> <expr01> after TimeOut -> <expr02> end