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
}
}
功能A和B都在不同的goroutine中。
说A是在RPC goroutine中。当应用程序收到RPC请求时,它将取消B中的旧计时器,并在另一个goroutine中启动一个新的计时器。
医生说:
停止不关闭通道,以防止从通道读取 成功不正确。
那么如何打破B中的选择以避免goroutine泄漏?
答案 0 :(得分:4)
使用额外的独立取消信号。由于您已经有一个select语句,另一个通道显然是一个选择:
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
}
}
请注意,所有As和B都会相互竞争计时器值。使用上面的代码,没有必要让A停止计时器,因此您不需要全局计时器,从而消除了竞争:
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
}
}
答案 1 :(得分:2)
添加上述答案,如果您想一次取消所有服务员,您可以使用您自己的可以取消的计时器机制封装行为,在After
频道中发送true或false告诉您对于所有服务员而言,无论是取消还是暂停。
package main
import (
"fmt"
"time"
)
type CancellableTimer struct {
cancel chan bool
}
func NewCancellableTimer() *CancellableTimer {
return &CancellableTimer{
cancel: make(chan bool),
}
}
// internal wait goroutine wrapping time.After
func (c *CancellableTimer) wait(d time.Duration, ch chan bool) {
select {
case <-time.After(d):
ch <- true
case <-c.cancel:
ch <- false
}
}
// After mimics time.After but returns bool to signify whether we timed out or cancelled
func (c *CancellableTimer) After(d time.Duration) chan bool {
ch := make(chan bool)
go c.wait(d, ch)
return ch
}
// Cancel makes all the waiters receive false
func (c *CancellableTimer) Cancel() {
close(c.cancel)
}
// a goroutine waiting for cancellation
func B(t *CancellableTimer) {
select {
// timedOut will signify a timeout or cancellation
case timedOut := <-t.After(time.Second):
if timedOut {
fmt.Println("Time out!")
} else {
fmt.Println("Cancelled!")
}
}
}
func main() {
t := NewCancellableTimer()
// Start 3 goroutines that wait for different timeouts on the same timer
go B(t)
go B(t)
go B(t)
// sleep a bit before cancelling
time.Sleep(100 * time.Millisecond)
// cancel the timer and all its waiters
t.Cancel()
// this is just to collect the output
time.Sleep(time.Second)
}
输出:
Cancelled!
Cancelled!
Cancelled!
游乐场链接: