SikiShen
SikiShen

Reputation: 61

How this select works in goroutine?

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?

Upvotes: 2

Views: 819

Answers (2)

hmoragrega
hmoragrega

Reputation: 222

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.

Upvotes: 1

leaf bebop
leaf bebop

Reputation: 8222

First, in the func fibonacci, the select statement tries select the first thing of the two following to finish:

  1. c <- x
  2. <- quit

It is fairly easy to understand <- quit, which is tries to receive a value from a channel called quit (and ignores the value received).

c <- x means sending a value that equals (is a copy of) x. It seems like unblocking, but in Go, sending over an unbuffered channel (which is explained in the Go tour) blocks when there is no receiver.

So here it means, waiting for a receiver to be ready to receive the value (or a space in the buffer, if it were a buffered channel), which in this code means fmt.Println(<-c), and then send the value to the receiver.

So this statement unblocks (finishes) whenever <-c is evaluated. That is, every iteration of the loop.

And for your code, while all value 1, 2, 3, is guaranteed to be sent over the channel (and received), func b returns and thus func main retains without guaranteeing the fmt.Println(3) finishes.

In Go, when the func main returns, the program terminates, and unfinished goroutine does not get a chance to finish its work - thus sometimes it prints 3 and soetimes it does not.

Upvotes: 1

Related Questions