V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Flourite
V2EX  ›  Go 编程语言

关于 go 定时器 reset 的问题

  •  
  •   Flourite · 20 小时 28 分钟前 · 575 次点击
    func main() {
        const timeout = 10 * time.Millisecond
        // 10ms 超时
        t := time.NewTimer(timeout)
        // 当前 goroutine 先挂起个 20ms
        time.Sleep(20 * time.Millisecond)
    
        // 到这时 t 已经过期
        start := time.Now()
    
        // 重置 timer ,设置 10ms 的超时时间
        t.Reset(timeout)
        // 理想:在这里阻塞,10ms 后被唤醒
        // t.C => make(chan Time, 1)
        <-t.C 
    
        fmt.Printf("Time elapsed: %dms\n", time.Since(start).Milliseconds())
        // 预期输出: Time elapsed: 10ms
        // 实际输出: Time elapsed:  0ms
    }
    

    go 声称 1.23 解决了 reset 的问题?但我在 1.24 下测试发现还是老样子。翻遍了源码,按道理 t 没有第一时间执行<-t.C 应该不满足 needsAdd 的条件,就不会放到 p.timers.heap 最小堆上,channel 不应该有数据才对

    9 条回复    2025-03-03 13:29:53 +08:00
    sagaxu
        1
    sagaxu  
       19 小时 53 分钟前
    1.23 开始用的是 unbuffered channel ,Reset()时取消了正阻塞中的写入吗?
    Flourite
        2
    Flourite  
    OP
       19 小时 17 分钟前
    @sagaxu 不是,还是 unbuffered channel ,reset 时会把 buf 的内容清空掉
    Flourite
        3
    Flourite  
    OP
       19 小时 14 分钟前
    说错了,channel 没有改,还是有缓存的`make(chan Time, 1)`
    sagaxu
        4
    sagaxu  
       18 小时 42 分钟前
    @Flourite 在 chan 的实现里改了,根据 asynctimerchan 变量做行为切换,src/runtime/chan.go
    Flourite
        5
    Flourite  
    OP
       10 小时 3 分钟前
    @sagaxu 在 channel 的 len 跟 cap 判断函数改了,但这不是重点,我反复看 needsAdd 的判断,都不满足加入最小堆的条件,那么 t.C 是什么时候被发送数据的
    bv
        6
    bv  
       9 小时 8 分钟前   ❤️ 1
    我试了,go1.23 1.24 中均输出 Time elapsed: 10ms ,这不正符合你的预期吗?
    https://go.dev/play/p/D2wJ4DNYjcs
    Flourite
        7
    Flourite  
    OP
       8 小时 35 分钟前
    @bv 发现问题了,我的 workspace 设置的是 1.22 ,即便我用 1.24 运行单文件,实际也跑的是 1.22 版...
    pike0002
        8
    pike0002  
       8 小时 16 分钟前
    是不是用 go.mod 了?把 go.mod 里面的版本改成 1.23 或之后
    lesismal
        9
    lesismal  
       4 小时 57 分钟前
    分两类:
    1. 同步的:使用 t.C 的地方配合了 select ,其他的 context 甚至 default 之类的可以先于 t.C 解除阻塞并 t.Reset ,这种依赖高版本可以解决问题。如果担心以为高版本没问题但实际用的低版本导致问题,可以自己封装下 Reset 用 select <-t.C: default 避免这种情况
    2. 异步的:很多场景是<-t.C 与 t.Reset 是不在同一个协程,而<-t.C 与 Reset 可能发生在同一个瞬间,Reset 并不能保证先于<-t.C 返回,这样就需要额外的一致性保障、除非这块的一致性要求不大
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3765 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 10:27 · PVG 18:27 · LAX 02:27 · JFK 05:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.