Fibers: different code results with the Guide

I am learning fibers, first the code beblow doesn’t work.

ch = Channel(Int32).new

spawn do
  puts "Before receive"
  puts ch.receive
end

# 10.times do |i|
#   puts "Before send"
#   ch.send i
# end

puts "Before send"
ch.send 1
puts "Before send"
ch.send 2

# output:
# Before send
# Before receive
# 1
# Before send
# ...main keeps blocking

I was not sure if I misunderstood how the fiber works. So I copied the code from the guide(https://crystal-lang.org/reference/guides/concurrency.html)

channel = Channel(Int32).new

spawn do
  puts "Before first send"
  channel.send(1)
  puts "Before second send"
  channel.send(2)
end

puts "Before first receive"
value = channel.receive
puts value # => 1

puts "Before second receive"
value = channel.receive
puts value # => 2

The outpput in my machine is:

Before first receive
Before first send
Before second send
1
Before second receive
2

Which is different to

Output in the guide:

Before first receive
Before first send
1
Before second receive
Before second send
2

Version:

$ crystal version
Crystal 0.31.1 [0e2e1d067] (2019-09-30)

LLVM: 8.0.0
Default target: x86_64-unknown-linux-gnu

Thanks.

The output depends on what happens when you send a value over a channel. If there’s a fiber waiting for that value, I think some time ago that fiber was resumed and the fiber sending the value was paused. Eventually I think waj found out that either behavior was okay and sending the value but continuing in the current fiber is more performant, or I’m not sure, and that’s the current behavior.

I think the docs should be changed to say “an output similar to this one”, but not say “exactly this one”. Both behaviors are valid and they depend on implementation details.

2 Likes

Thanks,there are some changes in 0.31 that I found in the changelog, but I still don’t know how to make this two code work as expected, they work in 0.30.

code link1

code link 2

The second receive is not printed out. Could you help me to figure that out, thanks.

When you do ch.send right at the end the program might end there because there’s no more main code. You can do Fiber.yield to switch to another fiber.

What are you trying to do? I can solve this example but it would be nice to know what you actually need.

Thanks, I am a frontend developer not familiar with concurrent, recently I am playing with crystal, write some nonsense code.

Here is what I thought, not sure If it is right.

  1. Channel#receive consumes data. If a fiber got the value, other fibers won’t get it.
  2. If the running time is on spawned fibers, it will run other fibers continually, but they won’t get the data since #1.
  3. Channel#send would switch fibers only when that fibers have not been run, since #2, sometimes it may need to call Fibers.yield to let other fibers consume data.

Ok that may be a totally incorrect description about fibers, but I tried my best, the behavior seems like magic when more “send” and “receive” add to the code, it is hard for me to know who will recieve, then where the execution flows to or blocking at.

I need to find more tutorials about this topic.

Hey @jacob, I suggest you think about it this way, to simplify things, until you familiarise yourself with communicating sequential processes a bit more.

  • Channel#send is a blocking operation, meaning that if no one is receiving on the other side, the running Fiber will suspend until a receiver shows up
  • Channel#receive is a blocking operation, too, meaning that the fiber will suspend until a fiber sends on the same channel.

I suggest you leave considerations around the order in which things run out of this, here is why:

  • in concurrent applications, the order in which fibers run might depend on the implementation - as @asterite mentioned
  • enabling the experimental Multi-Threading feature on the compiler will result in even more variability in the order in which fibers run/complete.

That said, the great news is, you don’t need to care about the order in which fibers run to write a correct concurrent application!
In absence of authoritative resources on Crystal’s concurrency, I suggest you have a look at what our neighbours go programmers preach. I’d recommend Concurrency in Go to get you started.

2 Likes

@lbarasti Thanks for your explanation and recommendation. They are very useful. I will write more code and read the “Concurrency in Go”, nice trick.

Thanks again.

1 Like