Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I receive values from a closed channel?

Tags:

concurrency

go

I'm investigating the channels behaviour and I'm quite confused by their behaviour. The spec say After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. However it seems I still get the values in the range statement even if by that time the channel is closed. Why is that?

package main

import "fmt"
import "sync"
import "time"

func main() {
    iCh := make(chan int, 99)
    var wg sync.WaitGroup
    go func() {
        for i := 0; i < 5; i++ {
            wg.Add(1)
            go func(i int) {
                defer wg.Done()
                iCh <- i
            }(i)

        }
        wg.Wait()
        close(iCh)
    }()
    time.Sleep(5 * time.Second)
    print("the channel should be closed by now\n")
    for i := range iCh {
        fmt.Printf("%v\n", i)
    }
    print("done")
}

Edit: It seems that if I move the close statement just before the channel range it closes it for good. So I'm wondering why it's not working with the "time.Sleep" trick too . By that time (5 seconds) all the go routines should have been completed and the channel closed, isn't it ?

like image 304
The user with no hat Avatar asked Mar 26 '15 02:03

The user with no hat


1 Answers

The Go Programming Language Specification

Close

For a channel c, the built-in function close(c) records that no more values will be sent on the channel. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking.

In the channel buffer there are 5 previously sent values followed by a close.

For example,

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    iCh := make(chan int, 99)
    var wg sync.WaitGroup
    go func() {
        for i := 0; i < 5; i++ {
            wg.Add(1)
            go func(i int) {
                defer wg.Done()
                iCh <- i
            }(i)

        }
        wg.Wait()
        close(iCh)
    }()

    time.Sleep(5 * time.Second)
    fmt.Println("previously sent values", len(iCh))
    for i := range iCh {
        fmt.Printf("%v\n", i)
    }
    print("the channel should be closed by now\n")
    print("done")
}

Output:

previously sent values 5
0
1
2
3
4
the channel should be closed by now
done
like image 129
peterSO Avatar answered Sep 22 '22 15:09

peterSO