Concurrency in Elixir
If you have read my previous articles, you may know I am a kind of a parallel computing geek. So this is the reason. If you have pay for a PC đ» and if that PC has two or more cores (Nowadays almost all the processor have more that one core), obviously you have to pay for those core even you are not consuming. In this case, I think you are a developer. Otherwise, itâs not up to you to decide whether you will consume all those cores đ. So if you have pay for those, why donât you consume it. Cores are expensive đž.
Still confused đ. Let me explain. First, letâs see how many cores do you have on your PC đ». If you are in a windows machine just open the Task Manager and navigate to the Performance tab. You can easily find how many cores do you have.
If you are in Linux just type nproc you will get the number of core you have, or type lscpu and you can find more information about your processor. I had 4 cores on my laptop when I was writing this article. That means, if I consider this, I could have consumed my processor 4 times more than Iâm consuming now.
Now I trust, as a developer, you have written millions of code lines in your laptop and have run. Now ask yourself, at least a single time did you consider consuming all those core with your awesome code. If the answer is no, donât be upset. In the beginning, I event didnât know there is such a thing called core rather than the processor.
The tragic story is this is the situation of many software companies. Just imagine, of all the developers out there (I mean careless developers like me đ) consider about consuming all those core, how much energy they could have save for the world, just by not running several servers to do a task which they could have been done with less number of servers đ. Per SE I said that it doesnât mean that they did not need to consume their servers efficiently. There are many practical issues when writing code in a way that using all the cores. It is hard to write the synchronized code, which is using all the cores. Read this article to learn much better, why it is a hard thing to write concurrent codes.
This is where Elixir comes to the stage. Elixir is an awesome programming language that is run on Erlang VM (BEAM), which helps you to write software with concurrency. If you have studied concurrent programming before, you might have heard, process, thread, thread pool, locks, mutual exclusion, race conditions, deadlock, etc. and these words like the silence before the storm. But trust me, Elixir will save you. Keep in mind, this doesnât mean, now it is a piece of cake to develop concurrent software with Elixir.
Highly concurrent systems arenât easy to write, but with Elixir, the language is on your side
Now letâs see whatâs happening to underline the Elixir to achieve concurrency. Elixir using the message passing method rather than shared memory and locks to communicate between processes (Read this article to learn more about inter-process communication). Elixir is using The Actor Model for this. An actor is a single-threaded process, which can make local decisions, create more actors, send more messages, and determine how to respond to the text message received.
Actor has these properties.
- Mail Box đ«
- Process ID đ
The mailbox is like the very mailbox in your house and the Process ID the same as the address of your house. Refer to this image.
Implementation
Create a new process
Now letâs see how to write concurrent code in Elixir with this actor model. Letâs create a sample function to learn this.
We can use spawn/3 function to run this process asynchronously.
iex> pid = spawn(Example, :add, [3, 5])
This will return the Process ID of the newly created process and write 8 to stdout. This newly created process is an Actor.
Message Passing
You already know inter-process communication makes concurrency much harder. Now letâs see how we can pass messages between processes in Elixir. In Elixir there are concepts for this: send/2 and receive. We can send messages to processes by their Process ID and receive using pattern matching to receive messages.
Now lets create a new process from this function and send some messages to that process.
iex> pid = spawn(Example, :messageReceiver, [])iex> send pid, {:ok, "hello"}
hello there
{:ok, "hello"} messageRiex> send pid, {:ok, "who are you"}
I am a Elixir Process
{:ok, "who are you"}iex> send pid, {:ok, "how are you"}
{:ok, "how are you"}
messageReceiver function catch {:ok, âhelloâ} and {:ok, âwho are youâ} messages and execute procedures bind to it.Here for {:ok, âhelloâ} message it writes âhello thereâ to stdout and for {:ok, âwho are youâ} message write âI am an Elixir processâ to stdout. messageReceiver function not looking for a message like {:ok, âhow are youâ} and it just doesnât care that message. You can see messageReceiver function working recursively not to stop after catching the first message.
Process linking
In Elixir we can use spawn_link to link a process to another process. So the parent process can notify if the child process crashed.
iex> Example.run()
Process crashed: reason - crashed
The run function creates a new process from iamgoingtocrash function and links that process to the parent process. When the child process crashed it parent can notify with {:EXIT, _from_pid, reason} type message.
These are the very basic concepts of implementing concurrency in Elixir. If you are interested to learn more read the following articles.
Now you may wonder, where are those consuming all the cores thing happen. BEAM is responsible for that. BEAM is the Erlang virtual machine and our Elixir code will run on that VM. Read this article to understand how BEAM designed to use all the cores in your PC.
This is all that Iâm going to discuss in this article. So if you want to become a parallel computing geek, you better add Elixir into your toolkit.