Golang Goroutine泄漏

huangapple go评论67阅读模式
英文:

Golang Goroutine leak

问题

这段代码是关于Go语言并发的一个解决方案。在Same()函数中,有一个注释说它会泄漏goroutine,这是什么意思呢?它还提到了一个修复这个问题的第二个文件。

在第一个文件中,Same()函数使用了两个goroutine来遍历两个树,并将它们的值发送到两个通道中。然后,通过从通道中接收值并比较它们,判断两个树是否包含相同的值。但是,如果两个树的结构不同,其中一个树的遍历会先结束,而另一个树的goroutine仍然在运行。这就是所谓的goroutine泄漏,因为没有正确地关闭goroutine。

为了解决这个问题,第二个文件中的代码对Walk()函数进行了修改。在walkImpl()函数中,使用了一个额外的quit通道来通知goroutine停止运行。当遍历到树的某个节点时,通过select语句同时监听chquit通道,如果quit通道有值可接收,就立即返回,停止继续遍历。这样,即使两个树的结构不同,也能正确地停止遍历并关闭所有的goroutine。

希望这能帮到你!如果还有其他问题,请随时提问。

英文:
// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"fmt"

	"code.google.com/p/go-tour/tree"
)

func walkImpl(t *tree.Tree, ch chan int) {
	if t == nil {
		return
	}
	walkImpl(t.Left, ch)
	ch <- t.Value
	walkImpl(t.Right, ch)
}

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
	walkImpl(t, ch)
	// Need to close the channel here
	close(ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
// NOTE: The implementation leaks goroutines when trees are different.
// See binarytrees_quit.go for a better solution.
func Same(t1, t2 *tree.Tree) bool {
	w1, w2 := make(chan int), make(chan int)

	go Walk(t1, w1)
	go Walk(t2, w2)

	for {
		v1, ok1 := <-w1
		v2, ok2 := <-w2
		if !ok1 || !ok2 {
			return ok1 == ok2
		}
		if v1 != v2 {
			return false
		}
	}
}

func main() {
	fmt.Print("tree.New(1) == tree.New(1): ")
	if Same(tree.New(1), tree.New(1)) {
		fmt.Println("PASSED")
	} else {
		fmt.Println("FAILED")
	}

	fmt.Print("tree.New(1) != tree.New(2): ")
	if !Same(tree.New(1), tree.New(2)) {
		fmt.Println("PASSED")
	} else {
		fmt.Println("FAILED")
	}
}

In this code, a solution for http://tour.golang.org/concurrency/8

Why is there a comment on Same() func Same(t1, t2 *tree.Tree) bool saying that it leaks goroutines? How so? It also mentions a second file that fixes this:

// Copyright 2015 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"fmt"

	"code.google.com/p/go-tour/tree"
)

func walkImpl(t *tree.Tree, ch, quit chan int) {
	if t == nil {
		return
	}
	walkImpl(t.Left, ch, quit)
	select {
	case ch <- t.Value:
		// Value successfully sent.
	case <-quit:
		return
	}
	walkImpl(t.Right, ch, quit)
}

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch, quit chan int) {
	walkImpl(t, ch, quit)
	close(ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
	w1, w2 := make(chan int), make(chan int)
	quit := make(chan int)
	defer close(quit)

	go Walk(t1, w1, quit)
	go Walk(t2, w2, quit)

	for {
		v1, ok1 := <-w1
		v2, ok2 := <-w2
		if !ok1 || !ok2 {
			return ok1 == ok2
		}
		if v1 != v2 {
			return false
		}
	}
}

func main() {
	fmt.Print("tree.New(1) == tree.New(1): ")
	if Same(tree.New(1), tree.New(1)) {
		fmt.Println("PASSED")
	} else {
		fmt.Println("FAILED")
	}

	fmt.Print("tree.New(1) != tree.New(2): ")
	if !Same(tree.New(1), tree.New(2)) {
		fmt.Println("PASSED")
	} else {
		fmt.Println("FAILED")
	}
}

How does it accomplish that? Where was this leak? (to test the code you will have to run it on http://tour.golang.org/concurrency/8). Very confused and would appreciate some help, thanks!

答案1

得分: 2

程序在检测到差异时停止接收通道上的数据。

walk协程会一直运行,直到它们被阻塞在发送数据到通道上。它们永远不会退出。这就是泄漏的原因。

英文:

The program stops receiving on the channels when a difference is detected.

The walk goroutines run until they block sending to the channels. They never exit. This is the leak.

答案2

得分: 0

第二种解决方案通过使用退出通道来处理泄漏。当退出通道关闭时(在Same()函数中),选择语句的第2个case成功(在walkImpl函数中的case <-quit),并且函数返回。因此,在程序退出后,walkImpl函数中没有阻塞。

英文:

The second solution handles the leak by using the quit channel. When the quit channel is closed (in Same() function), the case 2 of select statement succeeds(case <-quit in walkImpl function) and the function returns. Hence there is no block in the walkImpl function after the program exits.

huangapple
  • 本文由 发表于 2015年3月22日 10:35:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/29190333.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定