意外的Goroutine行为

意外的Goroutine行为,go,goroutine,Go,Goroutine,我是戈朗的初学者 我在读Go from中的并发性 事情一直进展顺利,直到有人向我提出关于 问题是:找出两个给定的二叉树是否相等。 我的方法是:按顺序遍历,将两棵树的值保存在一个切片中,然后比较它们 以下是我的解决方案:[不完整] package main import ( "fmt" "golang.org/x/tour/tree" ) // Walk walks the tree t sending all values // from the tree to the ch

我是戈朗的初学者

我在读Go from中的并发性

事情一直进展顺利,直到有人向我提出关于
问题是:找出两个给定的二叉树是否相等。
我的方法是:按顺序遍历,将两棵树的值保存在一个切片中,然后比较它们

以下是我的解决方案:[不完整]

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t != nil {
        Walk(t.Left, ch)
        ch <- t.Value
        Walk(t.Right, ch)
    }
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        fmt.Println("executing first go routing")
        Walk(t1, ch1)
        fmt.Println("closing channel [ch1]")
        close(ch1)
    }()

    go func() {
        fmt.Println("executing second go routing")
        Walk( t2, ch2 )
        fmt.Println("closing channel [ch2]")
        close(ch2)
    }()

    shouldContinue := true
    var continue1, continue2 bool
    for shouldContinue {
        select {
        case r1, ok1 := <-ch1:
            fmt.Println("[ch1] [rcvd]", r1)
            continue1 = ok1

        case r2, ok2 := <-ch2:
            fmt.Println("[ch2] [rcvd]", r2)
            continue2 = ok2
        }
        shouldContinue = continue1 || continue2
    }
    return true
}

func main() {
    Same(tree.New(1), tree.New(1))
}
有人能解释一下这是怎么回事吗?一旦通道2关闭,第二个go例程完成,为什么第一个不执行

任何帮助都将不胜感激。多谢各位

更新:
我在谷歌上搜索关于跳出频道的信息,我发现了一个很难回答的问题 据此,我更新了我的解决方案如下:

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
    // "time"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    // time.Sleep(time.Millisecond)
    if t != nil {
        Walk(t.Left, ch)
        ch <- t.Value
        Walk(t.Right, ch)
    }
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        fmt.Println("executing first go routing")
        Walk(t1, ch1)
        fmt.Println("closing channel [ch1]")
        close(ch1)
    }()

    go func() {
        fmt.Println("executing second go routing")
        Walk( t2, ch2 )
        fmt.Println("closing channel [ch2]")
        close(ch2)
    }()

    for {
        select {
        case r1, ok1 := <-ch1:
            fmt.Println("[ch1] [rcvd]", r1)
            if !ok1 {
                ch1 = nil
            } 

        case r2, ok2 := <-ch2:
            fmt.Println("[ch2] [rcvd]", r2)
            if !ok2 {
                ch2 = nil
            }
        }
        if ch1 == nil && ch2 == nil {
            break
        }
    }
    return true
}

func main() {
    Same(tree.New(1), tree.New(1))
}
我现在对正在发生的事情更加困惑

一旦通道2关闭,为什么第一个不被执行

不执行通道。反复执行的是您的选择。请注意,无论通道是否关闭,这两种情况都可以始终执行。因此,select可以选择第二个案例,id选择了该案例,您中止了该案例。(您的中止条件看起来可疑:一旦两个通道都关闭,即如果ok1和ok2都为false,则终止)

不要将select本身视为“goroutine调度工具”。事实并非如此。它将随机选择一个可运行的案例。如果您的所有案例都是
val形式,则确定:=
一旦通道2关闭,为什么第一个不被执行

不执行通道。反复执行的是您的选择。请注意,无论通道是否关闭,这两种情况都可以始终执行。因此,select可以选择第二个案例,id选择了该案例,您中止了该案例。(您的中止条件看起来可疑:一旦两个通道都关闭,即如果ok1和ok2都为false,则终止)


不要将select本身视为“goroutine调度工具”。事实并非如此。它将随机选择一个可运行的案例。若您的所有案例的形式都是
val,ok:=在代码的第一部分,则逻辑中存在错误

shouldContinue := true
var continue1, continue2 bool
for shouldContinue {
    select {
    case r1, ok1 := <-ch1:
        fmt.Println("[ch1] [rcvd]", r1)
        continue1 = ok1

    case r2, ok2 := <-ch2:
        fmt.Println("[ch2] [rcvd]", r2)
        continue2 = ok2
    }
    shouldContinue = continue1 || continue2
}
频道关闭后,您无法在此频道上发送值,但仍可从该频道接收。请参见此处:


Nil通道始终处于阻塞状态,并且您更改了
环路中断逻辑的
。这就是第二个解决方案工作的原因。

在代码的第一部分中,逻辑有错误

shouldContinue := true
var continue1, continue2 bool
for shouldContinue {
    select {
    case r1, ok1 := <-ch1:
        fmt.Println("[ch1] [rcvd]", r1)
        continue1 = ok1

    case r2, ok2 := <-ch2:
        fmt.Println("[ch2] [rcvd]", r2)
        continue2 = ok2
    }
    shouldContinue = continue1 || continue2
}
频道关闭后,您无法在此频道上发送值,但仍可从该频道接收。请参见此处:


Nil通道始终处于阻塞状态,并且您更改了
环路中断逻辑的
。这就是为什么您的第二个解决方案有效。

年轻玩家有用的书签:年轻玩家有用的书签:感谢您的努力。为什么第一个不被处决?这是我在问题后面纠正的一个打字错误。谢谢你的努力。为什么第一个不被处决?是一个打字错误,我后来在问题中更正了。谢谢。这个答案也很有帮助+谢谢你。这个答案也很有帮助+1.
shouldContinue := true
var continue1, continue2 bool
for shouldContinue {
    select {
    case r1, ok1 := <-ch1:
        fmt.Println("[ch1] [rcvd]", r1)
        continue1 = ok1

    case r2, ok2 := <-ch2:
        fmt.Println("[ch2] [rcvd]", r2)
        continue2 = ok2
    }
    shouldContinue = continue1 || continue2
}
continue1 = true
continue2 = true
for shouldContinue {
    select {
    case r1, ok1 := <-ch1:
        fmt.Println("[ch1] [rcvd]", r1)
        continue1 = ok1

    case r2, ok2 := <-ch2:
        fmt.Println("[ch2] [rcvd]", r2)
        continue2 = ok2
    }
    shouldContinue = continue1 || continue2
}