Golang Go语言中 goroutine 的泄露应该怎么处理

发布于 1周前 作者 zlyuanteng 来自 Go语言

Golang Go语言中 goroutine 的泄露应该怎么处理

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 &lt;- 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 中好像会有内存泄露,应该是回收的地方做的不对。

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


更多关于Golang Go语言中 goroutine 的泄露应该怎么处理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

16 回复

这不叫泄露,你代码本来就不限制并发量。

更多关于Golang Go语言中 goroutine 的泄露应该怎么处理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


刚刚看了你用的库的文档,例子里有 Stop 函数,ping 结束你调用下 Stop 函数看下呢。

生产者消费者模型找个教程看下吧,你没有限制消费者数量。

请问具体 goroutine 泄露的表现是什么呢?在 pprof 中发现 无论 ping 任务是否结束 goroutine 都保持在一个很高的数吗?

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

同样,如果在 pprof 的 goroutine 中会打印函数调用栈信息,可以发现究竟都在哪发生了阻塞,可以快速定位出造成阻塞的 goroutine 代码。

看了下 go-ping 这个库,猜测可能是并发量大导致了死锁,有 issue: https://github.com/sparrc/go-ping/issues/77,可以尝试按照 issue 描述的方式增加 channel 容量,看看能否解决.

#5 我主要是不确定,MultiPing 方法里面 for 死循环中,里面开 goroutine,这些 goroutine 会被回收吗,还是会泄露。我试试逻辑里面不去调用 ping,就直接把传入 PingIp 的值放入 ch 中,看看这种情况下是否会泄露。
#6 试过增加容量,还是有问题。应该不是。问题应该是 reus 所说的,要用一个 pool 池。

就问你 发请求的函数没有 ctx 参数 心里虚不虚

没细看, 猜测要在 PingIP 函数加超时 context 吧, 同意楼上

MultiPing 按照我的理解,是主 Goroutine 执行的,本身如果是死循环的话,主程序 不会终止。
此外就是楼上说到的,如果 ping 那边有阻塞的话,也会使得调用其的 goroutine 阻塞了。建议可以看看 ping 那边的超时参数


#9
#10 超时那边应该是 pinger.Timeout = time.Second * 1 这个吧,我设置了。

报告各位大佬,首先我的第一个实现版本是有问题。 现在我按照 http://jmoiron.net/blog/limiting-concurrency-in-go/ 这个里面的用法,以及用了 workerpool 的用法,我发现是不是这个 go-ping 库有问题啊,我把 PingIp 这个函数变为直接打印出来,就啥错误都没了。https://pastebin.com/89KknAMV

好吧 查出来了,我自己写的有问题。

具体是什么问题呢? 我看了下 go-ping 这个库, 似乎直接设置下 pinger.Timeout 就行

问题解决了要反馈下楼上的热心老哥




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

新写了一个版本,生产者读取 file.txt 送到一个 chan 里面去,然后 main 函数里面开 goroutine 作为消费者,消费 chan 里面的 ip 就行了。goroutine 的个数可以自己控制,也可以按照二楼老哥的方案,弄个池子。

在Go语言中,处理goroutine泄露是一个重要而复杂的任务。goroutine泄露通常发生在goroutine被错误地启动但没有被适当地终止或回收,导致系统资源(如内存和文件描述符)被不必要地占用,最终可能导致程序崩溃或性能下降。

要处理goroutine泄露,可以采取以下策略:

  1. 确保goroutine的终止:为每个goroutine设置一个明确的退出条件,并确保在适当的时候退出。这通常涉及使用通道(channel)或其他同步机制来通知goroutine何时停止工作。

  2. 使用context包:在Go 1.7及更高版本中,可以使用context包来管理goroutine的生命周期。通过传递一个context.Context对象给goroutine,可以在需要时取消它。

  3. 监控和分析:使用Go的内置工具(如pprof)或第三方工具来监控和分析goroutine的行为。这些工具可以帮助你识别哪些goroutine没有被正确终止。

  4. 代码审查和测试:在代码审查和测试阶段,特别注意那些启动goroutine的部分,确保它们有适当的退出和清理机制。

  5. 定期重构和优化:随着程序的发展,定期重构和优化代码,确保goroutine的使用是高效且安全的。

通过实施这些策略,你可以有效地减少或消除Go程序中的goroutine泄露问题。

回到顶部