quinnj
quinnj

Reputation: 1268

Noob on channels in Go

I'm trying to wrap my head around concurrency patterns in Go and was confused by this example from #69

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

In particular, I don't see how

for i := 0; i < 10; i++ {
    fmt.Println(<-c)
 }

is supposed to work, since all we did was make the channel, and now we "receive" from it 10 times? I tried out other code where I create a channel and then try to receive from it right away and I always get an error, but this seems to work and I can't quite see how. Thanks for any help!

Upvotes: 2

Views: 106

Answers (2)

Not_a_Golfer
Not_a_Golfer

Reputation: 49265

fmt.Println(<-c) will block until there's something to read from the channel. Since we start the for loop in a separate goroutine, it means the first iteration of the loop will simply sit idly and wait until there's something to read.

Then the fibonacci function starts, and pushes data down the channel. This will make the loop wake up and start printing.

I hope it makes better sense now.

Upvotes: 4

Dato
Dato

Reputation: 454

I’m giving you a shorter version of the code above, which I think should be easier to understand. (I explain the differences below.) Consider this:

// http://play.golang.org/p/5CrBSu4wxd
package main

import "fmt"

func fibonacci(c chan int) {
    x, y := 0, 1
    for {
        c <- x
        x, y = y, x+y
    }
}

func main() {
    c := make(chan int)
    go fibonacci(c)

    for i := 0; i < 10; i++ {
        fmt.Println(<-c)
    }
}

This is a more straightforward version because your main function is clearly just printing 10 values from the channel, and then exiting; and there is a background goroutine that is filling the channel as long as a new value is needed.

This alternate version drops the quit channel, because the background goroutine simply dies when main() finishes (no need to kill it explicitly in such a simple example).

Of course this version also kills the use of select{}, which is the topic of #69. But seeing how both versions accomplish the same thing, except killing of the background goroutine, can perhaps be a good aid in understanding what select is doing.

Note, in particular, that if fibonacci() had a time.Sleep() as its first statement, the for loop would hang for that much time, but would eventually work.

Hope this helps!

P.S.: Just realized this version is just a simpler version than #68, so I’m not sure how much it’ll help. Oops. :-)

Upvotes: 2

Related Questions