In traditional programming languages, concurrency can be challenging and often requires the implementation of complex synchronization mechanisms to prevent conflicts between threads. However, Erlang provides a built-in concurrency model that simplifies designing and implementing concurrent systems.
In Erlang, threads are also known as schedulers, and they are responsible for executing Erlang processes. Each scheduler can run multiple processes concurrently, and the number of schedulers can be configured based on the available hardware resources.
While processes and threads are used for concurrency in Erlang, the two terms usually have a key difference. Processes have their own memory space, whereas threads share memory space with other threads within the same process. However, generally, threads of execution in Erlang share no data, which is why they are usually called processes, and we can use the terms interchangeably if we aren’t diving deep into the specifics.
A process can send a message to another process using the !
operator, for example:
Pid ! Message
Here Pid
is the process ID of the receiving process, and Message
is the message being sent.
The receiving process can wait for incoming messages using the receive statement, which has the following format:
receive
Pattern1 ->
Actions1;
Pattern2 ->
Actions2;
...
PatternN ->
ActionsN
end.
Here, Pattern1
through PatternN
are patterns that incoming messages are matched against.
The first pattern matches an incoming message, and the corresponding actions are executed. If no pattern matches, the process blocks until a matching message is received.
Here is an example of a message passing between two processes that send messages to each other a number of times:
-module(main). -export([start/0, thread1/2, thread2/0]). thread1(0, Thread2_PID) -> Thread2_PID ! finished, io:format("Thread1 finished~n", []); thread1(N, Thread2_PID) -> Thread2_PID ! {thread1, self()}, receive thread2 -> io:format("Thread1 received message from Thread2~n", []) end, thread1(N - 1, Thread2_PID). thread2() -> receive finished -> io:format("Thread2 finished~n", []); {thread1, Thread1_PID} -> io:format("Thread2 received message from Thread1~n", []), Thread1_PID ! thread2, thread2() end. start() -> Thread2_PID = spawn(main, thread2, []), spawn(main, thread1, [3, Thread2_PID]).
In this example, thread1
and thread2
are two processes that send messages to each other. start/0
creates the two processes and allows them to communicate. The thread1/2
function sends messages to thread2
, and the thread2/0
function waits for incoming messages and responds to them.
Here’s an example of how this module can be used:
1> c(main).
2> main:start().
Thread2 received message from Thread1
Thread1 received message from Thread2
Thread2 received message from Thread1
Thread1 received message from Thread2
Thread2 received message from Thread1
Thread1 received message from Thread2
Thread1 finished
Thread2 finished
This output shows the messages sent and received by the two processes. The output begins with Thread2 received a message from Thread1
because thread2
is the first process that receives a message.
The message-passing mechanism allows threads/processes to communicate and synchronize in Erlang.
This approach ensures that processes run independently and asynchronously, avoiding common issues in other concurrent programming models, such as deadlocks, race conditions, and shared-memory inconsistencies.
Free Resources