Non Blocking Channel Operations in Crystal

Our program demonstrates non-blocking channel operations using select with a default clause. This allows us to implement non-blocking sends, receives, and multi-way selects.

require "channel"

messages = Channel(String).new
signals = Channel(Bool).new

# Here's a non-blocking receive. If a value is
# available on `messages` then `select` will take
# the `messages.receive` case with that value. If not
# it will immediately take the `default` case.
select
when msg = messages.receive
  puts "received message #{msg}"
else
  puts "no message received"
end

# A non-blocking send works similarly. Here `msg`
# cannot be sent to the `messages` channel, because
# the channel has no buffer and there is no receiver.
# Therefore the `else` case is selected.
msg = "hi"
select
when messages.send(msg)
  puts "sent message #{msg}"
else
  puts "no message sent"
end

# We can use multiple `when` clauses above the `else`
# clause to implement a multi-way non-blocking
# select. Here we attempt non-blocking receives
# on both `messages` and `signals`.
select
when msg = messages.receive
  puts "received message #{msg}"
when sig = signals.receive
  puts "received signal #{sig}"
else
  puts "no activity"
end

To run the program:

$ crystal run non_blocking_channel_operations.cr
no message received
no message sent
no activity

In Crystal, we use Channel to create channels. The select statement is used for non-blocking operations on channels. Instead of case, we use when for each channel operation, and else instead of default for the fallback case.

Note that Crystal’s channels are unbuffered by default, similar to Go. If you need buffered channels, you can create them with Channel(T).new(capacity).

Crystal’s concurrency model is based on fibers, which are lightweight threads managed by the Crystal runtime. This is similar to Go’s goroutines, allowing for efficient concurrent programming.