How this select works in goroutine?

Issue

i have been following the go tour examples, and i don’t understand how this works
https://tour.golang.org/concurrency/5

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)
}

how is this working?

and while i’m trying to understand.

package main
import "fmt"

func b(c,quit chan int) {
    c <-1
    c <-2
    c <-3
}

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

this will sometimes print 1,2 sometimes print 1,2,3, why?

Solution

To better understand the Fibonacci example let’s analyze the different parts.

First the anonymous function

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

The "go" keyword will start a new goroutine, so now we have the "main" goroutine and this one, they’re gonna be running concurrently.

The loop is telling us that we are going to execute this 10 times:

fmt.Println(<-c)

This call will block until we receive an integer from the channel and print it. Once this happens 10 times we are gonna signal that this goroutine has finished

quit <- 0

Now let’s go back to the main goroutine, while the other goroutine was starting it has invoked the function "finbonacci", there’s no "go" keyword so this call is blocking here.

fibonacci(c, quit)

Now let’s analyze the function

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

We start an infinite loop and on each iteration, we will try to execute one case from the select

The first case will try to send Fibonacci’s sequence values indefinitely to the channel, at some point, once the other goroutine reaches the fmt.Println(<-c) statement, this case will be executed, the values will be recalculated and the next iteration of the loop will happen.

case c <- x:
        x, y = y, x+y 

The second case won’t be able to run yet, since nobody is sending anything to the "quit" channel.

case <-quit: 
        fmt.Println("quit")
        return
}

After 10 iterations the first goroutine will stop receiving new values, thus the first select case won’t be able to run

case c <- x: // this will block after 10 times, nobody is reading
        x, y = y, x+y 

At this point, no case in the "select" can be executed, the main goroutine is "blocked" (more like paused from a logical point of view).

Finally, at some point, the first goroutine will signal that it has finished using the "quit" channel

quit <- 0

This allows the execution of the second "select" case, which will break the infinite loop and allow the "fibonacci" function to return and the main function will be able to finish in a clean way.

Hopefully, this helps you understand the Fibonacci example.

Now, moving to your code, you are not waiting for the anonymous goroutine to finish, in fact, it’s not even finishing. Your "b" method is sending "1,2,3" and returns immediately.

Since it’s the last part of the main function, the program terminates.

If you only see "1,2" sometimes is because the "fmt.Println" statement is too slow and the program terminates before it can print the 3.

Answered By – hmoragrega

Answer Checked By – Terry (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.