c1 = Channel(Int32).new
c2 = Channel(Nil).new
spawn do
(1..10).each do |i|
sleep(100.milliseconds)
c1.send(i)
end
c1.close
end
spawn do
loop do
if i = c1.receive?
puts i
else
c2.send(nil)
end
end
end
c2.receive
puts "Done"
could
loop do
if i = c1.receive?
...
end
end
be replaced by something like:
c1.each do |item|
puts item
end
perhaps via a macro
(Obviously , the inspiration is the iteration over a channel with a “for x:= range c1”) in Go.
Crystal’s stdlib Channel
isn’t meant to be used that way, but it can be with select
(very similar in concept to Go’s select
):
loop do
select
when value = channel.receive
yield value
else
break
end
end
Could probably have an Enumerable
object for this:
channel = Channel(String).new(10)
5.times { |i| channel.send i.to_s }
doubled = channel.range.map do |string|
string * 2
end
pp doubled: doubled
class Channel
def range
Range.new(self)
end
struct Range(T)
include Enumerable(T)
def initialize(@channel : Channel(T))
end
def each
loop do
select
when value = @channel.receive
yield value
else
break
end
end
end
end
end
Keep in mind, though, that select
on channels at a high-enough volume may not be feasible. Since stdlib channels need to be extremely flexible, they aren’t optimized for single-consumer workloads. Specifically, they can be consumed by any number of fibers safely, but “safe” doesn’t mean “non-blocking” and that safety comes at a performance cost.
If you just need to be able to consume a channel from a single fiber while other fibers write to it, I created a shard a while back called mpsc
(multi-producer/single-consumer) optimized for this purpose. Checking if the channel can be read without blocking (via receive?
) involves no heap allocations and the benchmark results are pretty wild:
➜ mpsc git:(master) ITERATIONS=1 crystal run --release benchmarks/mpsc_vs_select.cr
Empty channel
MPSC::Channel 430.59M ( 2.32ns) (± 2.00%) 0.0B/op fastest
Channel 6.71M (149.01ns) (± 0.92%) 288B/op 64.16× slower
The difference with a larger number of iterations per block invocation gets even more extreme:
➜ mpsc git:(master) ✗ ITERATIONS=1000 crystal run --release benchmarks/mspsc_vs_select.cr
Empty channel
MPSC::Channel 1.06M (941.00ns) (± 1.33%) 0.0B/op fastest
Channel 6.71k (148.94µs) (± 0.61%) 281kB/op 158.28× slower
4 Likes