所有goroutines都睡着了

时间:2017-01-14 18:50:25

标签: go goroutine channels

我正在尝试使用goroutines和频道

    package main

import (
    "fmt"
    "math/rand"
    "time"
)

func boring(msg string) <-chan string {
    c := make(chan string)
    go func() {
        for i := 0; ; i++ {
            c <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
        }
    }()
    return c
}
func main() {
    c := fanInNew(boring("joe"), boring("anh"))
    for i := 0; i < 10; i++ {
        fmt.Println(<-c)
    }
    fmt.Println("You both are boring, I am leaving")
}

func fanInNew(input1, input2 <-chan string) <-chan string {
    c := make(chan string)
    for {
        select {
        case s := <-input1:
            c <- s
        case s := <-input2:
            c <- s
        }
    }
    return c
}

如果我运行这个程序它会给我错误,所有goroutine都睡着了,死锁。

但如果我将select放在匿名goroutine中,它就可以了。工作示例:

    package main

import (
    "fmt"
    "math/rand"
    "time"
)

func boring(msg string) <-chan string {
    c := make(chan string)
    go func() {
        for i := 0; ; i++ {
            c <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
        }
    }()
    return c
}
func main() {
    c := fanInNew(boring("joe"), boring("anh"))
    for i := 0; i < 10; i++ {
        fmt.Println(<-c)
    }
    fmt.Println("You both are boring, I am leaving")
}

func fanInNew(input1, input2 <-chan string) <-chan string {
    c := make(chan string)
    go func() {
        for {
            select {
            case s := <-input1:
                c <- s
            case s := <-input2:
                c <- s
            }
        }
    }()
    return c
}

请你帮我理解背后的推理。

1 个答案:

答案 0 :(得分:2)

for语句永远循环,因此<-c chan永远不会传递,chans会被填充,但main线程会等待c := fanInNew(a, b)

fanInNew() 永不返回,因为for 永远循环(以及select btw上的阻止) :

func fanInNew(input1, input2 <-chan string) <-chan string {
    c := make(chan string)
    for { // Loop forever and read from inputs 1 and 2
        select {
        case s := <-input1:
            c <- s
        case s := <-input2:
            c <- s            
        }
    }
    return c
}

然后在主线程中,此函数永远不会返回c chan。

func main() {
    // Never gets passed the next line
    c := fanInNew(boring("joe"), boring("anh"))
}

所以你可以将for循环本身放在goroutines中,就像你在第二个例子中所做的那样。 同样通常goroutines应该返回,要么是因为你传递了一条消息(例如通过close() ing),要么是因为它们到达了一个return语句。

在任何情况下,您在第二个示例中所拥有的内容非常适合演示使用匿名闭包。传入goroutine的chan可以在其他地方返回,并在线程之间启用安全消息传递:

c := make(chan string)
go func() {
    for {
        select {
        case s := <-input1:
            c <- s
        case s := <-input2:
            c <- s
        }
    }
}()
return c

有几种方法可以在匿名goroutine中结束for循环,包括选择第二个chan结束频道close()时你可以返回。此外,通常WaitGroups可以实现这一目标。