使用频道时出现问题:所有goroutine都处于睡眠状态-死锁

时间:2020-09-06 07:36:05

标签: go channels

我试图了解渠道的运作方式。我有这个例子:

import (
    "fmt"
    "runtime"
    "time"
)

func greet(c chan string) {
    fmt.Println("1: " + <-c)
    fmt.Println("2: " + <-c)
}

func main() {
    fmt.Println("main() started")

    c := make(chan string, 1)

    go greet(c)
    c <- "John"

    c <- "Mike"
    c <- "Mary"
    fmt.Println("active goroutines", runtime.NumGoroutine())
    c <- "Edward"
    time.Sleep(time.Second)
    fmt.Println("main() stopped")
}

在执行上述代码时,我看到它给出了一个错误“致命错误:所有goroutine都在睡眠-死锁!”。但是据我了解,在“爱德华”发送到频道后,执行应该被阻塞。

c <- "Edward"

,该程序应已打印出Mary和Edward的值。我也不会在任何地方关闭此频道。谁能告诉我原因,我该如何解决?

2 个答案:

答案 0 :(得分:1)

请参考以下代码,以便您更好地理解它。我也添加了评论。虽然@mkopriva消除了您的疑虑,但我认为如果发布带有注释的代码会更好。

package main

import (
    "fmt"
    "time"
)

func greet(c chan string) {
    // Range over the channel until close() is called
    for r := range c {
        // Print the names
        fmt.Println(r)
    }
}

func main() {
    fmt.Println("main() started")
    c := make(chan string, 1) // Make a bufferred channel with capacity of 1
    go greet(c)               // Spawn the goroutine
    c <- "John"               // Send
    c <- "Mike"               // Send
    c <- "Mary"               // Send
    c <- "Edward"             // Send
    close(c)                  // Close the channel which signals the for loop
    // inside greet to stop receiving
    time.Sleep(2 * time.Second) // Let us give the main gorutine some time
    // before it exits so that it lets the greet() to recieve all the names.
    // But it's better to use sync.WaitGroup for this.
    fmt.Println("main() stopped")
}

答案 1 :(得分:0)

问题的答案非常简单。 如果您将某些内容放入频道,则必须以某种方式将其撤回。 只要在函数greet(c chan string)前面加一个“ go”,它就不会无限运行。这仅表示该函数正在调用新的go例程以在其中运行。 您编写的代码会产生死锁,因为该函数在通道范围内终止后终止。

go greet(c)
c <- "John"
c <- "Mike"
//all these things happen at the same time

当您将某个内容放到无法再次退出的频道中时,就会创建一个死锁。该线程尝试将某些东西放到不再有使用者的渠道中。

解决方案也非常简单。只需在频道上循环即可。这样,它会在整个通道上一直传播,直到关闭为止。

func greet(c chan string) {
    for name := range c {
        fmt.Println(name)
    }
}