Reputation: 57381
I've adapted this example, https://gobyexample.com/tickers, to the following script:
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
go func() {
for {
select {
case <-done:
fmt.Println("Received 'done'")
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()
time.Sleep(1600 * time.Millisecond)
// ticker.Stop()
done <- true
// fmt.Println("Ticker stopped.")
}
Two differences with the referenced example are that I've commented out the ticker.Stop()
line and added a fmt.Println("Received 'done'")
line in the case <-done
block. If I run this, I observe the following output:
> go run tickers.go
Tick at 2019-10-06 15:25:50.576798 -0700 PDT m=+0.504913907
Tick at 2019-10-06 15:25:51.074993 -0700 PDT m=+1.003102855
Tick at 2019-10-06 15:25:51.576418 -0700 PDT m=+1.504521538
My question: why does it not print Received 'done'
to the terminal?
Strangely, if I comment in the Ticker stopped
Println statement, I do see Received 'done'
as well:
> go run tickers.go
Tick at 2019-10-06 15:27:30.735163 -0700 PDT m=+0.504666656
Tick at 2019-10-06 15:27:31.234076 -0700 PDT m=+1.003573649
Tick at 2019-10-06 15:27:31.735342 -0700 PDT m=+1.504833296
Ticker stopped.
Received 'done'
As a I recall, the code within a Goroutine can be assuming to run synchronously, so I'm perplexed that I don't see the effect of the Println
statement in the former case since it happens before the Goroutine returns. Can someone explain this?
Upvotes: 0
Views: 2003
Reputation: 487725
... why does it not print Received 'done' to the terminal?
It does—or rather, it tries.
A Go program exits when the main goroutine (which called main
of package main
) returns (or sooner in various cases not occurring here). Your main
returns after calling time.Sleep()
and then sending true
on done
.
Meanwhile, the goroutine that's in the for
loop awakens when the true
value arrives on the done
channel. This happens after the main goroutine has sent it, after which the main goroutine is in the process of exiting.
If, in this process of exiting, the main goroutine takes long enough, the anonymous goroutine will have time to print Received 'done'
. If, in this process of exiting, the main goroutine is speedy enough, the anonymous goroutine never finishes, or maybe never even starts, printing anything, and you see nothing. (The actual output is accomplished by a single underlying system call so you either get all of it, or none of it.)
You can make sure the goroutine you spun off finishes before having your main one exit by any number of mechanisms, but the simplest is probably to use sync.WaitGroup
since it's kind of designed for that. Create a waitgroup, set its counter to 1 (add 1 to its initial zero), and then call the Done
function on the way out of the anonymous goroutine. Have the main goroutine wait for it.
Upvotes: 1
Reputation: 711
When done <- true
happened, the main function returned directly.
You could add another time.Sleep() to see what happened.
time.Sleep(1600 * time.Millisecond)
// ticker.Stop()
done <- true
// fmt.Println("Ticker stopped.")
time.Sleep(1600 * time.Millisecond)
Upvotes: 2