Here's a piece of code which outputs a list of integers as pushed to a channel. Else a select checks and prints necessary timeout message.
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
c := make(chan int)
go readFromChannel(c, time.After(time.Duration(2)*time.Second))
// time.Sleep(time.Duration(5) * time.Second) //Talking about uncommenting this line
c <- 10
c <- 20
c <- 30
c <- 40
c <- 50
wg.Wait()
}
func readFromChannel(c chan int, ti <-chan time.Time) {
defer wg.Done()
go func() {
for {
select {
case x := <-c:
fmt.Println("Read", x)
case t :=<-ti:
fmt.Println("TIMED OUT with "+t.String())
}
}
}()
}
Also here's the playground link for the same : https://play.golang.org/p/4hNWze4Pfwr This code outputs list of integer like
Read 10
Read 20
Read 30
Read 40
Read 50
But when I un-comment the line that make the main routine go to sleep for 5 sec(mentioned in the code as comment/line number 16), output changes to :
TIMED OUT with 2009-11-10 23:00:02 +0000 UTC m=+2.000000001
Read 10
Read 20
Read 30
Read 40
I want to understand why the last Read 50
is not being printed in second case.
The problem is that your wg.Done()
is in the wrong place. It must be in your goroutine, but you're executing it before the goroutine even starts, so your program is liable to exit before doing any work.
Change this:
defer wg.Done()
go func() {
to this:
go func() {
defer wg.Done()
Of course then you'll have an infinitely running goroutine, because your for
loop has no exit condition. You'll need to add one, probably by checking for the channel closing:
select {
case x, ok := <-c:
if !ok { // channel was closed
return
}
fmt.Println("Read", x)
case t :=<-ti:
fmt.Println("TIMED OUT with "+t.String())
}
Then tell your main go routine to close the channel when it's done:
c <- 40
c <- 50
close(c) // We're done, so tell the goroutine to finish up
wg.Wait() // But wait until it's done