[GAME] Print a b c sequentially and repeatedly use Fiber/Channel

Saw a blog post from a friend, which print a b c a b c … a b c sequentially and repeatedly use goroutine, it wrote use Chinese, but, you can check the golang source code anyway.

so, i want to do the same things use Crystal Fiber/Channel.

Following is two example, hope it can inspire others, good ideas, and make me understand Fiber/Channel more deeper.

example1:

It works! but i consider this solution depends on the internal implementation details of scheduler, if i am not wrong, the spawn run earlier in following code will push into scheduler earlier too, and Crystal always take the proc from scheduler from start to end sequentially and rotately.

  ch = Channel(Nil).new
  ch1 = Channel(Nil).new
  ch2 = Channel(Nil).new

  def printa
    puts "a"
    sleep 0.5
  end

  def printb(ch)
    puts "b"
    sleep 0.5
    ch.send nil
  end

  def printc(ch)
    puts "c"
    sleep 0.5
    ch.send nil
  end

  spawn do
    loop do
      ch1.receive
      ch2.receive
      ch.send nil
    end
  end

  loop do
    printa
    spawn printb(ch1)
    spawn printc(ch2)
    ch.receive
  end

example2:

this solution should be logically guaranteed. but i consider code is verbose …

done = Channel(Nil).new
done_print = Channel(Nil).new
ch1 = Channel(Nil).new
ch2 = Channel(Nil).new
ch3 = Channel(Nil).new

def printa(ch)
  ch.send nil
  puts "a"
  sleep 0.5
end

def printb(ch)
  ch.send nil
  puts "b"
  sleep 0.5
end

def printc(ch, done_print)
  ch.send nil
  puts "c"
  sleep 0.5
  done_print.send nil
end

spawn do
  loop do
    ch1.receive
    ch2.receive
    ch3.receive
    done.send nil
    done_print.receive
  end
end

loop do
  spawn printa(ch1)
  spawn printb(ch2)
  spawn printc(ch3, done_print)
  done.receive
end
1 Like

you can translate first example from Go to Crystal almost one to one:

bagA = Channel(Nil).new
bagB = Channel(Nil).new
bagC = Channel(Nil).new

spawn do
  loop do
    bagA.receive
    puts "a"
    bagB.send nil
  end
end

spawn do
  loop do
    bagB.receive
    puts "b"
    bagC.send nil
  end
end

spawn do
  loop do
    bagC.receive
    puts "c"
    bagA.send nil
  end
end

# initiate loop
bagA.send nil

# this is to don't terminate app because main fiber ended.
loop do 
  Fiber.yield
end
3 Likes

Cool, really impressive and easy to understand.

It is obvious that your solution is more robust than mine!

In fact, I never check the origin go code, because whenever I see golang’s ugly code, headache, a real headache. :)

2 Likes