Elixir School - Intermediate
Concurrency
Processes in the Erlang VM are lightweight and can be ran across all available CPUs of the machine it is being ran on
defmodule Example do
def add(a, b) do
IO.puts(a + b)
end
end
Example.add(2, 3)
To create a new process in the Erlang VM, use the spawn/1
or spawn/3
functions, which return a Process Identifier (PID)
spawn(Example, :add, [2, 3])
Message Passing
For two processes to communicate, the functions send/2
and receive/1
allow the programmer to create this communication logic
defmodule Example do
def listen do
receive do
{:ok, "hello"} -> IO.puts("World")
end
listen()
end
end
pid = spawn(Example, :listen, [])
send(pid, {:ok, "hello"})
send(pid, :ok)
Process.exit(pid, :normal)
Process Linking
spawn_link/1
, or spawn_link/3
, can be used to “link” two processes so they can be “aware” of each other
Two linked processes will receive exit notifications from one another
defmodule Example do
def explode, do: exit(:kaboom)
end
# spawn(Example, :explode, [])
# spawn_link(Example, :explode, [])
defmodule Example do
def explode, do: exit(:kaboom)
def run do
Process.flag(:trap_exit, true)
spawn_link(Example, :explode, [])
receive do
{:EXIT, _from_pid, reason} -> IO.puts("Exit reason: #{reason}")
end
end
end
Example.run()
Process Monitoring
spawn_monitor/1
, or spawn_monitor/3
, can be used to be notified when a process crashes without the current process crashing or needing to explicitly trap exits
defmodule Example do
def explode, do: exit(:kaboom)
def run do
spawn_monitor(Example, :explode, [])
receive do
{:DOWN, _ref, :process, _from_pid, reason} -> IO.puts("Exit reason: #{reason}")
end
end
end
Example.run()
Agents
An abstraction around background processes maintaining state
{:ok, agent} = Agent.start_link(fn -> [1, 2, 3] end)
Agent.update(agent, fn state -> state ++ [4, 5, 6] end)
Agent.get(agent, & &1)
Agent.start_link(fn -> [1, 2, 3] end, name: Numbers)
Agent.get(Numbers, & &1)
Tasks
Provides a way to execute a function in the background and retrieve its value later
Useful when handling expensive operations without blocking the application execution
defmodule Example do
def double(x) do
:timer.sleep(2000)
x * 2
end
end
task = Task.async(Example, :double, [2000])
Task.await(task)