fatal error: all goroutines are asleep - deadlock

fatal error: all goroutines are asleep - deadlock

我有以下 Go 代码

package main

import (
    "fmt"
    "math/rand"
)

const (
    ROCK int = iota
    PAPER
    SCISSORS
)

type Choice struct {
    Who   int //0 you 1 your opponent
    Guess int
}

//Win returns true if you win.
func Win(you, he int) bool {
    ...
}

func Opponent(guess chan Choice, please chan struct{}) {
    for i := 0; i < 3; i++ {
        <-please
        choice := rand.Intn(3)
        who := 1
        guess <- Choice{who, choice}
        please <- struct{}{}
    }
}

func GetChoice(you, he int) int {
    ...
}

var Cheat func(guess chan Choice) chan Choice = func(guess chan Choice) chan Choice {
    new_guess := make(chan Choice)
    // go func() {
    for i := 0; i < 3; i++ {
        g1 := <-guess
        g2 := <-guess
        if g1.Who == 0 {
            choice := GetChoice(g1.Guess, g2.Guess)
            new_guess <- g2
            new_guess <- Choice{g1.Who, choice}
        } else {
            choice := GetChoice(g2.Guess, g1.Guess)
            new_guess <- g1
            new_guess <- Choice{g2.Who, choice}
        }
    }
    // }()
    fmt.Println("...")
    return new_guess
}

func Me(guess chan Choice, please chan struct{}) {

    for i := 0; i < 3; i++ {
        <-please
        choice := rand.Intn(3)
        who := 0
        guess <- Choice{who, choice}
        please <- struct{}{}
    }
}

func Game() []bool {
    guess := make(chan Choice)
    //please sync 2 goroutines.
    please := make(chan struct{})
    go func() { please <- struct{}{} }()
    go Opponent(guess, please)
    go Me(guess, please)
    guess = Cheat(guess)
    var wins []bool

    for i := 0; i < 3; i++ {
        g1 := <-guess
        g2 := <-guess
        win := false
        if g1.Who == 0 {
            win = Win(g1.Guess, g2.Guess)
        } else {
            win = Win(g2.Guess, g1.Guess)
        }
        wins = append(wins, win)
    }

    return wins
}

func main() {
    win := Game()
    fmt.Println(win)
}

当我 运行 这段代码时,我得到错误 致命错误:所有 goroutines 都睡着了 - 死锁!。但是,当我取消注释 go func() 行中的 Cheat 函数时,错误消失了。我无法理解为什么错误出现在第一种情况下以及为什么在使用 goroutine 时错误消失了。那么,如果有人可以解释一下吗?

在这个简化的例子中:

func Cheat(guess chan Choice) chan Choice {
        new_guess := make(chan Choice)
        new_guess <- Choice{}
        <-guess
        return new_guess
}

当写入新分配的通道时,没有其他人可能拥有该通道,因此写入将永远阻塞。由于该写入块,从 guess 读取永远不会发生。但是,在您引用的代码中,Cheat() 函数是唯一从 guess 通道读取的函数;所以写入它的东西在读取发生时被阻塞,读取不会发生直到写入 new_guess 发生,并且写入不会发生直到包含函数 returns。

如果将通道 I/O 移动到 goroutine 中,则包含函数可以 return 在事情进展之前,因此 Cheat() 中的写入与读取配对Game() 结束,事情可以继续进行。