Non Blocking Channel Operations in Elixir
Basic sends and receives on channels are blocking in Elixir. However, we can use receive
with a after
clause to implement non-blocking receives, and send
with Process.info(self(), :message_queue_len)
to implement non-blocking sends.
defmodule NonBlockingChannelOperations do
def run do
messages_pid = spawn(fn -> receive_loop() end)
signals_pid = spawn(fn -> receive_loop() end)
# Here's a non-blocking receive. If a value is
# available on `messages_pid` then `receive` will take
# that value. If not, it will immediately take the `after` clause.
receive do
{:message, msg} ->
IO.puts("received message #{msg}")
after
0 -> IO.puts("no message received")
end
# A non-blocking send works similarly. Here `msg`
# is sent to the `messages_pid` process, and we check
# if the message was sent by examining the process mailbox.
msg = "hi"
send(messages_pid, {:message, msg})
if Process.info(messages_pid, :message_queue_len) == {:message_queue_len, 1} do
IO.puts("sent message #{msg}")
else
IO.puts("no message sent")
end
# We can use multiple patterns above the `after` clause
# to implement a multi-way non-blocking receive. Here we
# attempt non-blocking receives on both `messages_pid` and `signals_pid`.
receive do
{:message, msg} ->
IO.puts("received message #{msg}")
{:signal, sig} ->
IO.puts("received signal #{sig}")
after
0 -> IO.puts("no activity")
end
end
defp receive_loop do
receive do
_ -> receive_loop()
end
end
end
NonBlockingChannelOperations.run()
To run the program:
$ elixir non_blocking_channel_operations.exs
no message received
no message sent
no activity
In this Elixir version, we use processes to simulate channels. The spawn
function creates new processes that run the receive_loop
function, which keeps the processes alive and ready to receive messages.
We use receive
with an after 0
clause to implement non-blocking receives. For non-blocking sends, we send the message and then check the process mailbox length to determine if the message was sent successfully.
The multi-way non-blocking select is implemented using multiple patterns in the receive
block, followed by an after 0
clause for the default case.
Note that Elixir’s concurrency model is based on the actor model, which is somewhat different from Go’s channel-based concurrency. However, this example demonstrates how to achieve similar non-blocking operations in Elixir.