A few days ago I asked Does it have a crystal-lang Queue?, and @Johannes Müller answered me for using Channel::Buffered(T)
. Now, I have a littlebit another question. What if I have more than one producer? And all of them using the same Channel
?
For example:
channel = Channel(String).new
# first producer
spawn do
5.times do |i|
puts "first producer #{i}"
channel.send "first #{i}"
sleep 0.1
end
end
# second producer, that send data to the same with first producer channel
spawn do
5.times do |i|
puts "second producer #{i}"
channel.send "second #{i}"
sleep 0.1
end
end
# consumer
spawn do
loop do
data = channel.receive
puts "receive: #{data}"
sleep 0.5
end
end
sleep 6
Output will be:
$ crystal ./test.cr
first producer 0 # ok. first produced 0
second producer 0 # ok. second produced 0
receive: first 0 # ok. received from the first producer
first producer 1 # o_O. Where received data from the second producer?
receive: first 1
first producer 2
receive: first 2
first producer 3
receive: first 3
first producer 4
receive: first 4
receive: second 0 # aa. It's here... Why it's happend only, when the first producer was produced all?
second producer 1
receive: second 1
second producer 2
receive: second 2
second producer 3
receive: second 3
second producer 4
receive: second 4
As you can see, the "second producer" send first package almost at the same time with the first, but it's was ignored untill the first producer finished the job. And doesn't matter, channel buffered or not.
Why did not they work consistently? If the first producer, works in an eternal cycle, data from the second producer never been received.
Probably this behavior should be, and send data to one place from several - obviously bad practice?
Not sure if i properly understood what you meant, but is this the expected output?:
First producer sent: 0
Consumer received "First: 0"!
Second producer sent: 0
Consumer received "Second: 0"!
First producer sent: 1
Consumer received "First: 1"!
Second producer sent: 1
Consumer received "Second: 1"!
First producer sent: 2
Consumer received "First: 2"!
Second producer sent: 2
Consumer received "Second: 2"!
First producer sent: 3
Consumer received "First: 3"!
Second producer sent: 3
Consumer received "Second: 3"!
First producer sent: 4
Consumer received "First: 4"!
Second producer sent: 4
Consumer received "Second: 4"!
If so; this is the code:
class CrystalIsAwesome
@@products = 0
@@channel = Channel(String).new
def self.produce(which, times, produce_time = 0)
@@products += times
spawn do
times.times do |i|
puts "#{which} producer sent: #{i}"
@@channel.send "#{which}: #{i}"
sleep produce_time.seconds
end
end
end
# first producer
produce "First", 5
# second producer
produce "Second", 5
@@products.times do |_|
puts %(Consumer received "#{@@channel.receive}"!)
end
end
Try adding a third producer (produce "Third", 2, 1
) and see how it plays out.
Again; see the Concurrency docs for why this works like this.