So to break it down, the first question is why you're seeing 8100 printed first. That's quite simple: the routine that pushed values 0-10 to the naturals channel is not started yet by the time you reach naturals <- 90. The channels are not buffered, so that routine waits until the value 90 is read by the routine that squares the values, and only then will you be writing the values 0-10 to the channel. This isn't guaranteed to happen all the time, but starting a routine means the runtime (in particular the scheduler) has to do some work. This work takes a bit of time, during which the main routine starts another 2 routines, and writes to the channel. That's why 90 is the first value to go through the channel, rather than 0.
Next, you're noticing not all your values are printed. If you want all values to be printed, you should close, or write to the done channel when your routine printing the output has completed. instead of having done <- struct{}{} in the routine that writes to naturals, you should move that to the routine where you read from squares. To signal that the naturals and squares channels are "done", you can simply close them:
Copygo func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
close(naturals) // we'll get to this in a bit
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
go func() {
for x := range squares {
fmt.Println(x)
}
done <- struct{}{}
}()
<-done // now we know everything has been printed
close(done) // always best to close all channels explicitly
Now this works like a treat, but we have a problem. There's no reliable, safe way to write our 90 value to the naturals channel except for moving that to the routine where we write values 0-10. You could get around that problem by re-using the done channel:
Copygo func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
done <- struct{}{} // signal this routine is done
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
go func() {
for x := range squares {
fmt.Println(x)
}
close(done) // done channel is closed
}()
<-done // now we know everything has been printed
naturals <- 90 // now we can write to this channel
close(naturals) // signal to the square routine that was the last value
<-done // wait for the channel to close
This works, and will print the values in the order you want/expect, with 8100 as the last value, but it's a bit of a mess. Relying on a channel like done here to mean different things is a bit of a pain. We're using it to signal both that we've written all our values to the naturals channel, and then the second time to indicate that we've printed all values. That's smelly. Thankfully, we have other means of knowing when one or more routines have finished: sync.WaitGroup:
Copywg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for x := 0; x <= 10; x++ {
naturals <- x
}
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
go func() {
for x := range squares {
fmt.Println(x)
}
close(done) // just close it
}()
wg.Wait() // wait for the first routine to return
naturals <- 90 // now we can write to naturals
close(naturals) // we're done, let the squares routine return, and close its channel
// this in turn will trigger the printer routine to return
<- done // wait for everything to be printed
There's a lot more that can be said about this particular use of channels, who "owns" them, and what routine is responsible for closing which channel, why using buffered channels here might be a good idea (depending on what you're actually wanting to do/accomplish), etc... for now, though, this should help you get going and hopefully shed some light on what's going on.
Answer from Elias Van Ootegem on Stack OverflowSo to break it down, the first question is why you're seeing 8100 printed first. That's quite simple: the routine that pushed values 0-10 to the naturals channel is not started yet by the time you reach naturals <- 90. The channels are not buffered, so that routine waits until the value 90 is read by the routine that squares the values, and only then will you be writing the values 0-10 to the channel. This isn't guaranteed to happen all the time, but starting a routine means the runtime (in particular the scheduler) has to do some work. This work takes a bit of time, during which the main routine starts another 2 routines, and writes to the channel. That's why 90 is the first value to go through the channel, rather than 0.
Next, you're noticing not all your values are printed. If you want all values to be printed, you should close, or write to the done channel when your routine printing the output has completed. instead of having done <- struct{}{} in the routine that writes to naturals, you should move that to the routine where you read from squares. To signal that the naturals and squares channels are "done", you can simply close them:
Copygo func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
close(naturals) // we'll get to this in a bit
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
go func() {
for x := range squares {
fmt.Println(x)
}
done <- struct{}{}
}()
<-done // now we know everything has been printed
close(done) // always best to close all channels explicitly
Now this works like a treat, but we have a problem. There's no reliable, safe way to write our 90 value to the naturals channel except for moving that to the routine where we write values 0-10. You could get around that problem by re-using the done channel:
Copygo func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
done <- struct{}{} // signal this routine is done
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
go func() {
for x := range squares {
fmt.Println(x)
}
close(done) // done channel is closed
}()
<-done // now we know everything has been printed
naturals <- 90 // now we can write to this channel
close(naturals) // signal to the square routine that was the last value
<-done // wait for the channel to close
This works, and will print the values in the order you want/expect, with 8100 as the last value, but it's a bit of a mess. Relying on a channel like done here to mean different things is a bit of a pain. We're using it to signal both that we've written all our values to the naturals channel, and then the second time to indicate that we've printed all values. That's smelly. Thankfully, we have other means of knowing when one or more routines have finished: sync.WaitGroup:
Copywg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for x := 0; x <= 10; x++ {
naturals <- x
}
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
go func() {
for x := range squares {
fmt.Println(x)
}
close(done) // just close it
}()
wg.Wait() // wait for the first routine to return
naturals <- 90 // now we can write to naturals
close(naturals) // we're done, let the squares routine return, and close its channel
// this in turn will trigger the printer routine to return
<- done // wait for everything to be printed
There's a lot more that can be said about this particular use of channels, who "owns" them, and what routine is responsible for closing which channel, why using buffered channels here might be a good idea (depending on what you're actually wanting to do/accomplish), etc... for now, though, this should help you get going and hopefully shed some light on what's going on.
You probably want something like this:
Copypackage main
import (
"fmt"
)
func main() {
naturals := make(chan int)
squares := make(chan int)
done := make(chan struct{})
doneSending := make(chan struct{})
// counter
go func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
doneSending <- struct{}{}
}()
// squarer
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
// printer
go func() {
for x := range squares {
fmt.Println(x)
}
done <- struct{}{}
}()
<-doneSending
naturals <- 90
close(naturals)
<-done
}
Playground
They key is to have a chain of steps and make sure each one completes before signalling the next that it is done. Same for the entire program: don't exit before the last one is done.
Note however, that channels are a synching mechanism. An unbuffered channel can only be written to or be read from when there is a routine on the other end reading or writing to it "in that moment" (it is a bit more complex).
If a goroutine comes to such a point that it needs to wait, the Go runtime will most likely perform a context switch to another goroutine that is ready to be executed. A context switch has a certain cost and is one of the factors why using goroutines to perform tasks in parallel is often slower than just doing all in one routine. One way to reduce the number of context switches is to have buffered channels. That sacrifices a bit of memory (buffer) so that a gourine can write to a channel e.g. 10 messages (when the buffer is 10) before being blocked if the reading goroutine is not yet ready to read from it. And the reading routine can read all 10 messages from the buffer before it is blocked again. If both are reading and writing at the same time equally fast with a few items in the buffered channel, a context switch can theoretically be avoided entirely. (Still the Go runtime will perform context switches to give other goroutines a chance to run their code, given that there are other goroutines waiting)
Lately I've been learning go and I'm really liking it so far. I think Go fits the needs of a JavaScript programmer that wants to go lower level but not as low as c/c++.Currently I'm using Go by example for learning purposes.
I was learning channels and it's taking me a while to wrap my head around it.
I'm not understanding why the following code with channels works they way it does.
Part 1 taken from Range over channels section
Part 2 taken from Worker pools section
I modified the Part 1 and instead of closing the channel before iterating over it, I closed it after the iteration, resulting in a "all goroutines are asleep - deadlock!" error message in runtime. It looks like you cant iterate over a channel that's not closed. Fair enough.
Then I stumbled upon worker pools and in the example the worker is iterating over jobs channel even though its not closed?
Clearly my understanding of channels is not correct.
Why is Part 2 working well and the modified version of Part 1 (iterate over a channel that's not closed) is not?
Thank you in advance