如何正确停止计时器?

lxy*_*cls 2 go goroutine

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
    }
}
Run Code Online (Sandbox Code Playgroud)

函数A和B都在不同的goroutine中。

假设A在RPC goroutine中。当应用程序收到RPC请求时,它将取消B中的旧计时器,并在另一个goroutine中启动新计时器。

医生说:

停止不会关闭通道,以防止错误地从通道读取数据。

那么如何打破B中的选择以避免goroutine泄漏呢?

Pet*_*ter 5

使用附加的独立消除信号。由于您已经有一条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
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,所有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
    }
}
Run Code Online (Sandbox Code Playgroud)