您好,欢迎来到爱站旅游。
搜索
您的当前位置:首页Go 内存泄露之痛,这篇把 Go timer.After 问题根因讲透了!

Go 内存泄露之痛,这篇把 Go timer.After 问题根因讲透了!

来源:爱站旅游

大家好,我是煎鱼。

前几天在公众号分享了一篇 Go timer 源码解析的文章《难以驾驭的 Go timer,一文带你参透计时器的奥秘》。

如果大家也有兴趣共同交流,欢迎关注煎鱼的公众号,加我微信后拉你进群。

在评论区有小伙伴提到了经典的 timer.After 泄露问题,希望我能聊聊,这是一个不能不知的一个大 “坑”。

今天煎鱼就带大家来研讨一下这个问题。

timer.After

今天是男主角是Go 标准库 time 所提供的 After 方法。函数签名如下:

func After(d Duration) <-chan Time 

该方法可以在一定时间(根据所传入的 Duration)后主动返回 time.Time 类型的 channel 消息。

在常见的场景下,我们会基于此方法做一些计时器相关的功能开发,例子如下:

func main() {
    ch := make(chan string)
    go func() {
        time.Sleep(time.Second * 3)
        ch <- "脑子进煎鱼了"
    }()

    select {
    case _ = <-ch:
    case <-time.After(time.Second * 1):
        fmt.Println("煎鱼出去了,超时了!!!")
    }
}

在运行 1 秒钟后,输出结果:

煎鱼出去了,超时了!!!

上述程序在在运行 1 秒钟后将触发 time.After 方法的定时消息返回,输出了超时的结果。

坑在哪里

从例子来看似乎非常正常,也没什么 “坑” 的样子。难道是 timer.After 方法的虚晃一?

我们再看一个不像是有问题例子,这在 Go 工程中经常能看见,只是大家都没怎么关注。

代码如下:

func main() {
    ch := make(chan int, 10)
    go func() {
        in := 1
        for {
            in++
            ch <- in
        }
    }()
    
    for {
        select {
        case _ = <-ch:
            // do something...
            continue
        case <-time.After(3 * time.Minute):
            fmt.Printf("现在是:%d,我脑子进煎鱼了!", time.Now().Unix())
        }
    }
}

在上述代码中,我们构造了一个 for+select+channel 的一个经典的处理模式。

同时在 select+case 中调用了 time.After 方法做超时控制,避免在 channel 等待时阻塞过久,引发其他问题。

看上去都没什么问题,但是细心一看。在运行了一段时间后,粗暴的利用 top 命令一看:

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- azee.cn 版权所有 赣ICP备2024042794号-5

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务