Concurrency & channels

The program below prints 6 times the value 3.
I want it to print 1 2 3 1 2 3 , because the sleep takes 1 second , and that is longer than going throug the loop. I must have missed something .

puts "start"
channel = Channel(String).new
n = 0

3.times do
  n = n + 1
  spawn {
    m = n
    channel.send m.to_s
    sleep 1
    channel.send m.to_s
  }
end

6.times do
  puts channel.receive
end
puts "stop"

Tip: use block codes. Please read this: https://discourse.stonehearth.net/t/discourse-guide-code-formatting/30587

1 Like

My guess is the variable “n” is being overridden (those fibers don’t start till much later, after it’s already been overridden). You can get some details by reading through https://crystal-lang.org/reference/guides/concurrency.html (you can work around it with “block arguments” for example).

Would also be grand if the compiler could warn for cases like this. Or maybe even incorporate ameba into the standard compiler? (though to be fair it also missed this particular case) :)

Cheers!

Yes, that’s the reason. spawn just captures the block and executes it eventually, not immediately. By the time it runs, n is already 3 and m is assigned that.

If you move m = n to just above spawn it does what you want. The reason is that m gets a different value of n in each block invocation.

1 Like
n = 0
3.times do
  n = n + 1
  m = n
  spawn do
    puts n - m
  end
end
sleep 1

Very contra-intuitive for me , the program above prints 2 1 0.
Like it waits until n has its final value.

Maybe it’s just a simple race …

Yes, it’s a bit unintuitive.

But also, using idiomatic constructs you don’t get that problem, and the program is shorter:

puts "start"
channel = Channel(String).new

(1..3).each do |i|
  spawn {
    channel.send i.to_s
    sleep 1
    channel.send i.to_s
  }
end

6.times do
  puts channel.receive
end
puts "stop"
1 Like
n = 0
10.times do
  n = n + 1
  m = n
  Thread.new do
    puts n - m
  end
  sleep n % 2
end
sleep 1

I start to understand it , timing is essential. The program above prints,
0,1,0,1,0,1,0,1,0,0