为什么我需要运行一个新的子程序来执行步行?

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

Why do I need to run Walk with a new subroutine?

问题

我正在编写Go教程中的Walk函数,它基本上按顺序遍历一棵树。我已经写好了以下代码:

package main

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

// Walk函数遍历树t,并将所有的值发送到通道ch。
func Walk__helper(t *tree.Tree, ch chan int) {
    if (t == nil) {
        return
    }
    
    Walk__helper(t.Left, ch)
    ch <- t.Value
    Walk__helper(t.Right, ch)
}

func Walk(t *tree.Tree, ch chan int) {
    Walk__helper(t, ch)
    close(ch)
}

func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)
    for v := range ch {
        fmt.Println(v)   
    }
}

为什么我必须使用go Walk(tree.New(1), ch)而不是只用Walk(tree.New(1), ch)

我原本以为go关键字只是创建一个新线程。在这种情况下,如果子例程没有完成,我们可能会遇到问题,因为for循环可能会在其之前运行。

奇怪的是,当我去掉go关键字时,会出现死锁。这对我来说相当反直觉。这里的go关键字到底是做什么的?

英文:

I’m writing the Walk function in the go tutorial that basically traverses a tree in-order. What I have works:

package main

import (
    &quot;fmt&quot;
    &quot;code.google.com/p/go-tour/tree&quot;
)
    
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk__helper(t *tree.Tree, ch chan int) {
    if (t == nil) {
        return
    }
    
 	Walk__helper(t.Left, ch)
    ch &lt;- t.Value
    Walk__helper(t.Right, ch)
}

func Walk(t *tree.Tree, ch chan int) {
 	Walk__helper(t, ch)
    close(ch)
}

func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)
    for v := range ch {
     	fmt.Println(v)   
    }
}

Why must I use go Walk(tree.New(1), ch) instead of just Walk(tree.New(1), ch)?

I was under the impression that the go keyword basically spawns a new thread. In that case, we’d run into issues since the for loop might run before the subroutine completes.

Strangely, when I take out the go keyword, I get a deadlock. This is rather counterintuitive to me. What exactly is the go keyword doing here?

答案1

得分: 4

关键点在于range与通道(channel)结合使用时。

当你在一个通道上使用range(在这种情况下是ch),它会等待通道上的项目被发送,然后再迭代循环。这是一个安全的、"阻塞"的操作,在等待通道接收项目时不会发生死锁。

如果不使用goroutine,死锁就会发生,因为你的通道没有缓冲区。如果不使用goroutine,那么方法调用是同步的,Walk将某些内容放入通道中...并且它会阻塞,直到该内容被弹出。但它永远不会被弹出...因为方法调用是同步的。

我的理解是go关键字基本上会生成一个新的线程

..这是不正确的。要理解其中发生的事情,还需要了解更多重要的实现细节。你应该将goroutine的思维过程与线程分开..只将goroutine视为一个并发执行的代码片段,而没有"线程"的概念。

英文:

The key point here is range when coupled with a channel.

When you range over a channel (in this case, ch), it will wait for items to be sent on the channel before iterating through the loop. This is a safe, "blocking" action, that will not deadlock while it waits for the channel to receive an item.

The deadlock occurs when not using a goroutine because your channel isn't buffered. If you don't use a goroutine, then the method call is synchronous, Walk puts something on the channel.. and it blocks until that is popped off. It never gets popped off.. because the method call was synchronous.

> I was under the impression that the go keyword basically spawns a new thread

..that is incorrect. There are many more important implementation details required to understand what goes on there. You should separate your thought process of a goroutine from a thread.. and just think of a goroutine as a concurrently executing piece of code, without a "thread".

huangapple
  • 本文由 发表于 2014年9月19日 07:18:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/25923961.html
匿名

发表评论

匿名网友

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

确定