Golang Goroutine泄漏

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

Golang Goroutine leak

问题

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

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

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

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

英文:
  1. // Copyright 2012 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build ignore
  5. package main
  6. import (
  7. "fmt"
  8. "code.google.com/p/go-tour/tree"
  9. )
  10. func walkImpl(t *tree.Tree, ch chan int) {
  11. if t == nil {
  12. return
  13. }
  14. walkImpl(t.Left, ch)
  15. ch <- t.Value
  16. walkImpl(t.Right, ch)
  17. }
  18. // Walk walks the tree t sending all values
  19. // from the tree to the channel ch.
  20. func Walk(t *tree.Tree, ch chan int) {
  21. walkImpl(t, ch)
  22. // Need to close the channel here
  23. close(ch)
  24. }
  25. // Same determines whether the trees
  26. // t1 and t2 contain the same values.
  27. // NOTE: The implementation leaks goroutines when trees are different.
  28. // See binarytrees_quit.go for a better solution.
  29. func Same(t1, t2 *tree.Tree) bool {
  30. w1, w2 := make(chan int), make(chan int)
  31. go Walk(t1, w1)
  32. go Walk(t2, w2)
  33. for {
  34. v1, ok1 := <-w1
  35. v2, ok2 := <-w2
  36. if !ok1 || !ok2 {
  37. return ok1 == ok2
  38. }
  39. if v1 != v2 {
  40. return false
  41. }
  42. }
  43. }
  44. func main() {
  45. fmt.Print("tree.New(1) == tree.New(1): ")
  46. if Same(tree.New(1), tree.New(1)) {
  47. fmt.Println("PASSED")
  48. } else {
  49. fmt.Println("FAILED")
  50. }
  51. fmt.Print("tree.New(1) != tree.New(2): ")
  52. if !Same(tree.New(1), tree.New(2)) {
  53. fmt.Println("PASSED")
  54. } else {
  55. fmt.Println("FAILED")
  56. }
  57. }

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:

  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build ignore
  5. package main
  6. import (
  7. "fmt"
  8. "code.google.com/p/go-tour/tree"
  9. )
  10. func walkImpl(t *tree.Tree, ch, quit chan int) {
  11. if t == nil {
  12. return
  13. }
  14. walkImpl(t.Left, ch, quit)
  15. select {
  16. case ch <- t.Value:
  17. // Value successfully sent.
  18. case <-quit:
  19. return
  20. }
  21. walkImpl(t.Right, ch, quit)
  22. }
  23. // Walk walks the tree t sending all values
  24. // from the tree to the channel ch.
  25. func Walk(t *tree.Tree, ch, quit chan int) {
  26. walkImpl(t, ch, quit)
  27. close(ch)
  28. }
  29. // Same determines whether the trees
  30. // t1 and t2 contain the same values.
  31. func Same(t1, t2 *tree.Tree) bool {
  32. w1, w2 := make(chan int), make(chan int)
  33. quit := make(chan int)
  34. defer close(quit)
  35. go Walk(t1, w1, quit)
  36. go Walk(t2, w2, quit)
  37. for {
  38. v1, ok1 := <-w1
  39. v2, ok2 := <-w2
  40. if !ok1 || !ok2 {
  41. return ok1 == ok2
  42. }
  43. if v1 != v2 {
  44. return false
  45. }
  46. }
  47. }
  48. func main() {
  49. fmt.Print("tree.New(1) == tree.New(1): ")
  50. if Same(tree.New(1), tree.New(1)) {
  51. fmt.Println("PASSED")
  52. } else {
  53. fmt.Println("FAILED")
  54. }
  55. fmt.Print("tree.New(1) != tree.New(2): ")
  56. if !Same(tree.New(1), tree.New(2)) {
  57. fmt.Println("PASSED")
  58. } else {
  59. fmt.Println("FAILED")
  60. }
  61. }

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:

确定