代码之家  ›  专栏  ›  技术社区  ›  pmoubed

这是使用计时器的goroutine泄漏吗?

go
  •  -1
  • pmoubed  · 技术社区  · 8 月前

    请参阅gobyexample.com中的示例: https://gobyexample.com/timers

    timer2 := time.NewTimer(time.Second)
    go func() {
        <-timer2.C
        fmt.Println("Timer 2 fired")
    }()
    stop2 := timer2.Stop()
    if stop2 {
        fmt.Println("Timer 2 stopped")
    }
    

    问题:停止计时器后 go func() 永远卡住?如果是,处理这个案件的正确方法是什么?

    2 回复  |  直到 8 月前
        1
  •  1
  •   Burak Serdar    8 月前

    如果定时器在触发前停止,则goroutine将继续等待。您必须使用其他通道或上下文来停止它:

    done:=make(chan struct{})
    go func() {
        select {
            case <-timer2.C:
                fmt.Println("Timer 2 fired")
            case <-done:
        }
    }()
    stop2 := timer2.Stop()
    close(done)
    
        2
  •  1
  •   Schwern    8 月前

    停止计时器后,go func()会永远卡住吗?

    对。

    如果是,处理这个案件的正确方法是什么?

    你可以保持代码不变;只要不会导致死锁或泄漏,goroutine可以无限期地阻塞。

    如果你想让goroutine继续,请参阅 Burak Serdar's answer 。如果你想让函数在定时器到期后(而不是停止后)做点什么,请使用 AfterFunc .

        timer_func := func() { fmt.Println("Timer 2 After Func fired") }
        timer2 := time.AfterFunc(time.Second, timer_func)
    
        time.Sleep(2 * time.Second)
    
        stop2 := timer2.Stop()
        if stop2 {
            fmt.Println("Timer 2 stopped")
        }
    

    请注意,Go 1.23中最近进行了修复。从 time.Timer.Stop ...

    对于使用NewTimer(d)创建的基于chan的计时器,从Go 1.23开始,在Stop返回后从t.C收到的任何接收都保证会被阻止,而不是从Stop之前收到过时的时间值;如果程序尚未收到t.C的消息,并且计时器正在运行,则Stop保证返回true。在Go 1.23之前,使用Stop的唯一安全方法是插入一个额外的<-t.C如果Stop返回false以清空潜在的过时值。有关更多详细信息,请参阅NewTimer文档。

    time.Timer.NewTimer ...

    在Go 1.23之前,与计时器关联的通道是异步的(缓冲的,容量1),这意味着即使在计时器之后也可以接收过时的时间值。停止或定时器。重置已返回。从Go 1.23开始,通道是同步的(无缓冲,容量为0),消除了这些过时值的可能性。