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

这种情况下 goroutine 的泄露应该怎么处理

  •  
  •   matrix67 · 2020-03-09 20:46:20 +08:00 · 2307 次点击
    这是一个创建于 1711 天前的主题,其中的信息可能已经有所发展或是发生改变。
    package main
    
    import (
    	"bufio"
    	"fmt"
    	"github.com/sparrc/go-ping"
    	"log"
    	"os"
    	"time"
    )
    
    type PingRestult struct {
    	ip   string
    	time time.Duration
    }
    
    func MultiPing(urls []string, ch chan PingRestult) {
    	for {
    		for _, url := range urls {
    			go PingIp(url, ch)
    		}
    		time.Sleep(1 * time.Second)
    	}
    
    }
    
    func GetResult(ch chan PingRestult) {
    	for {
    		fmt.Println(<-ch)
    	}
    }
    
    func PingIp(ip string, ch chan PingRestult) {
    	pinger, err := ping.NewPinger(ip)
    	pinger.SetPrivileged(true)
    	if err != nil {
    		panic(err)
    	}
    	pinger.Count = 1
    	pinger.Timeout = time.Second * 1
    
    	pinger.Run()
    	// blocks until finished
    	stats := pinger.Statistics()
    	result := PingRestult{ip, stats.AvgRtt}
    	ch <- result
    }
    
    func main() {
    	file, err := os.Open("file.txt")
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer file.Close()
    
    	var lines []string
    	scanner := bufio.NewScanner(file)
    	for scanner.Scan() {
    		lines = append(lines, scanner.Text())
    	}
    
    	if err := scanner.Err(); err != nil {
    		log.Fatal(err)
    	}
    
    	ch := make(chan PingRestult, len(lines))
    	defer close(ch)
    
    	go GetResult(ch)
    	MultiPing(lines, ch)
    
    	select {}
    
    }
    

    在 file.txt 有 ip 列表,取出来,放在一个死循环中得到 ping 的结果。

    但是 MultiPing 中好像会有内存泄露,应该是回收的地方做的不对。

    问问大佬们这种咋处理。感谢。

    15 条回复    2020-03-11 19:59:35 +08:00
    reus
        1
    reus  
       2020-03-09 21:16:01 +08:00
    这不叫泄露,你代码本来就不限制并发量。
    reus
        2
    reus  
       2020-03-09 21:16:57 +08:00   ❤️ 1
    guonaihong
        3
    guonaihong  
       2020-03-09 21:19:33 +08:00   ❤️ 1
    刚刚看了你用的库的文档,例子里有 Stop 函数,ping 结束你调用下 Stop 函数看下呢。
    Mohanson
        4
    Mohanson  
       2020-03-09 21:23:08 +08:00 via Android   ❤️ 1
    生产者消费者模型找个教程看下吧,你没有限制消费者数量。
    zhuyuefeng
        5
    zhuyuefeng  
       2020-03-10 00:42:51 +08:00
    请问具体 goroutine 泄露的表现是什么呢?在 pprof 中发现 无论 ping 任务是否结束 goroutine 都保持在一个很高的数吗?

    还是在执行 ping 的任务时很多,执行完数量就回落了呢?

    同样,如果在 pprof 的 goroutine 中会打印函数调用栈信息,可以发现究竟都在哪发生了阻塞,可以快速定位出造成阻塞的 goroutine 代码。
    GreyYang
        6
    GreyYang  
       2020-03-10 09:20:58 +08:00
    看了下 go-ping 这个库,猜测可能是并发量大导致了死锁,有 issue: https://github.com/sparrc/go-ping/issues/77,可以尝试按照 issue 描述的方式增加 channel 容量,看看能否解决.
    matrix67
        7
    matrix67  
    OP
       2020-03-10 09:59:02 +08:00
    @zhuyuefeng #5 我主要是不确定,MultiPing 方法里面 for 死循环中,里面开 goroutine,这些 goroutine 会被回收吗,还是会泄露。我试试逻辑里面不去调用 ping,就直接把传入 PingIp 的值放入 ch 中,看看这种情况下是否会泄露。
    @GreyYang #6 试过增加容量,还是有问题。应该不是。问题应该是 reus 所说的,要用一个 pool 池。
    zunceng
        8
    zunceng  
       2020-03-10 14:13:29 +08:00
    就问你 发请求的函数没有 ctx 参数 心里虚不虚
    hzzhzzdogee
        9
    hzzhzzdogee  
       2020-03-10 14:21:01 +08:00
    没细看, 猜测要在 PingIP 函数加超时 context 吧, 同意楼上
    zhuyuefeng
        10
    zhuyuefeng  
       2020-03-10 14:22:53 +08:00
    @matrix67 MultiPing 按照我的理解,是主 Goroutine 执行的,本身如果是死循环的话,主程序 不会终止。
    此外就是楼上说到的,如果 ping 那边有阻塞的话,也会使得调用其的 goroutine 阻塞了。建议可以看看 ping 那边的超时参数
    matrix67
        11
    matrix67  
    OP
       2020-03-10 15:03:21 +08:00
    @zunceng
    @hzzhzzdogee #9
    @zhuyuefeng #10 超时那边应该是 pinger.Timeout = time.Second * 1 这个吧,我设置了。

    报告各位大佬,首先我的第一个实现版本是有问题。 现在我按照 http://jmoiron.net/blog/limiting-concurrency-in-go/ 这个里面的用法,以及用了 workerpool 的用法,我发现是不是这个 go-ping 库有问题啊,我把 PingIp 这个函数变为直接打印出来,就啥错误都没了。https://pastebin.com/89KknAMV
    matrix67
        12
    matrix67  
    OP
       2020-03-10 16:06:52 +08:00
    好吧 查出来了,我自己写的有问题。
    hzzhzzdogee
        13
    hzzhzzdogee  
       2020-03-10 16:22:06 +08:00
    @matrix67 具体是什么问题呢? 我看了下 go-ping 这个库, 似乎直接设置下 pinger.Timeout 就行
    kuro1
        14
    kuro1  
       2020-03-11 16:32:51 +08:00
    @matrix67 问题解决了要反馈下楼上的热心老哥
    matrix67
        15
    matrix67  
    OP
       2020-03-11 19:59:35 +08:00
    @hzzhzzdogee
    @kuro1

    我这个版本,写的不太好。MultiPing 这边 for 循环里面写了个 go PingIp,相当于无限消费者。所以内存爆炸了。

    新写了一个版本,生产者读取 file.txt 送到一个 chan 里面去,然后 main 函数里面开 goroutine 作为消费者,消费 chan 里面的 ip 就行了。goroutine 的个数可以自己控制,也可以按照二楼老哥的方案,弄个池子。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5285 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 01:19 · PVG 09:19 · LAX 17:19 · JFK 20:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.