前往频道和延迟

I was just experimenting with Go channels on my Ubuntu 64 bit environment and got confused with the output the following program produced.

I got the output: 0 1 2 3 Exit

The output when I uncommented the two commented lines: 0 1 2 3 4 Exit

Please explain the behavior. TIA.

package main

import (
"fmt"
    //"time"
)

func main() {
    ch := make(chan int)
    done := make(chan bool)
    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
        }
                //time.Sleep(1 * time.Second)
        done <- false
    }()
    go func() {
        for {
            select {
            case message := <-ch:
                fmt.Println(message)
            case <-done:
                return
            }
        }
    }()
    <-done
    fmt.Println("Exit")
}

You're not waiting for both goroutines, and only sending a single value over done to 2 receivers, which will deadlock if the second receiver happens to be main.

Using a WaitGroup simplifies the code, and allows you to easily wait for as many goroutines as needed. https://play.golang.org/p/MWknv_9AFKp

ch := make(chan int)
var wg sync.WaitGroup

wg.Add(1)
go func() {
    defer wg.Done()
    defer close(ch)
    for i := 0; i < 5; i++ {
        ch <- i
    }
}()

wg.Add(1)
go func() {
    defer wg.Done()
    for message := range ch {
        fmt.Println(message)
    }

}()

wg.Wait()
fmt.Println("Exit")

Your main thread is waiting on done, then exiting. Meanwhile your first go function pipes 5 values into ch, then sends to done.

The value in done then gets read from the main thread and happens to occur before the second go function reads the last value from ch. When it does so, it exits the program.

Note that if your second thread did happen to both read from ch and done, then your program would deadlock since the main thread would never receive on done and all running go threads would be blocked waiting to receive on channels.

You have two go routines running in parallel. One inserts 5 numbers into the channel and then signals the main thread to exit, and another one reads the numbers from the channel.

Note that once the go routine that is responsible for enqueuing the numbers into the channel finishes, it signals the main thread to exit, regardless of whether the go routine that reads the numbers finished or not. So you could end up with a case where the enqueueing go routine finished before the dequeuing finished, and the main thread exited.

By adding the sleep, you make the enqueueing go routine live a bit longer and give a chance to the dequeueing go routine to read and print all the numbers before the enqueueing go routine signals the main thread to exit.

To solve that, you could just run the dequeuing code in the main thread. No need to run it in a go routine in this case.