var timer *time.Timer
func A() {
timer.Stop() // cancel old timer
go B() // new timer
}
func B() {
timer = time.NewTimer(100 * time.Millisecond)
select {
case <- timer.C:
// do something for timeout, like change state
}
}
Function A and B are all in different goroutines.
Say A is in a RPC goroutine. When application receives RPC request, it will cancel the old timer in B, and start a new timer in another goroutine.
The doc say:
Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
So how to break the select in B to avoid goroutine leak?
Use an additional, independent cancellation signal. Since you already have a select statement in place, another channel is an obvious choice:
import "time"
var timer *time.Timer
var canceled = make(chan struct{})
func A() {
// cancel all current Bs
select {
case canceled <- struct{}{}:
default:
}
timer.Stop()
go B() // new timer
}
func B() {
timer = time.NewTimer(100 * time.Millisecond)
select {
case <-timer.C:
// do something for timeout, like change state
case <-canceled:
// timer aborted
}
}
Note that all As and Bs race against each other for the timer value. With the code above it is not necessary to have A stop the timer, so you don't need a global timer, eliminating the race:
import "time"
var canceled = make(chan struct{})
func A() {
// cancel all current Bs
select {
case canceled <- struct{}{}:
default:
}
go B()
}
func B() {
select {
case <-time.After(100 * time.Millisecond):
// do something for timeout, like change state
case <-canceled:
// aborted
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With