【问题标题】:All channels are deadlocked所有通道都死锁
【发布时间】:2018-09-20 20:10:16
【问题描述】:

我正在尝试使用一个 for 循环,该循环不断地将字符串发送到由不同 goroutine 读取的不同通道。但是,它给了我错误“所有 goroutines 都在睡觉 - 死锁!”为什么会这样?我搜索了一些答案,但找不到适合这种情况的答案。

func main() {
    var chans []chan string

    for i := 0; i < 3; i++ {
        chans = append(chans, make(chan string))
    }

    for i := 0; i < 3; i++ {
        go sendString(chans[i])
    }

    for str := range chans[0] {
        fmt.Print(str)
    }
}

func sendString(ch chan string) {
    ch <- "a"
    ch <- "b"
    ch <- "c"
    ch <- "d"
}

错误在这里。它打印“abcd”,然后产生错误,如果我删除循环打印,程序不会产生错误。

abcdfatal error: all goroutines are asleep - deadlock!

 goroutine 1 [chan receive]:
main.main()
    C:/Users/YuanZheng Hu/Desktop/Go Test/test/test.go:18 +0x28d

goroutine 19 [chan send]:
main.sendString(0xc04203c120)
    C:/Users/YuanZheng Hu/Desktop/Go Test/test/test.go:24 +0x42
created by main.main
    C:/Users/YuanZheng Hu/Desktop/Go Test/test/test.go:15 +0x175

goroutine 20 [chan send]:
main.sendString(0xc04203c180)
    C:/Users/YuanZheng Hu/Desktop/Go Test/test/test.go:24 +0x42
created by main.main
    C:/Users/YuanZheng Hu/Desktop/Go Test/test/test.go:15 +0x175
exit status 2

我使用 WaitGroup 做了版本,但它似乎不正确,并且给了我同样的错误“所有 goroutines 都睡着了 - 死锁!”我在下面的代码中哪里做错了?

func main() {
var myWaitGroup sync.WaitGroup
ch := make(chan string)
myWaitGroup.Add(1)
go sendString(ch, &myWaitGroup)


myWaitGroup.Wait()
close(ch)

time.Sleep(1 * time.Second)
}
func sendString(ch chan string, pg *sync.WaitGroup) {
ch <- "a"
ch <- "b"
ch <- "c"
ch <- "d"
defer pg.Done()

}

【问题讨论】:

  • @TrippKinetics 我猜对于那个问题,他关闭了通道,并使用另一个输出结果,但我想说的是,有没有办法只使用打印通道中的元素一个通道,不使用另一个 goroutine 中的接收函数
  • 如果你想把所有的字符串放到一个单独的通道中,你可以使用一个 sync.WaitGroup 来等待所有的 goroutine 完成然后在 main goroutine 中关闭这个通道

标签: go channel


【解决方案1】:

第二个 for 循环将阻塞,直到通道关闭,因此您需要在发送函数中将其关闭。此外,您仅从第一个通道读取,因此会丢失一些数据。这样做:

func main() {
    var chans []chan string

    for i := 0; i < 3; i++ {
        chans = append(chans, make(chan string))
    }

    for i := 0; i < 3; i++ {
        go sendString(chans[i])
    }

    for i := 0; i < 3; i++ {
        for str := range chans[i] {
            fmt.Print(str)
        }
    }

}
func sendString(ch chan string) {
    ch <- "a"
    ch <- "b"
    ch <- "c"
    ch <- "d"
    close(ch)
}

将产生:

abcdabcdabcd

https://play.golang.org/p/7SoDKChnTbz

如果您想根据您的评论使用单个频道执行此操作,那么您可以添加一个等待组以在所有 go 例程完成后关闭该频道:

func main() {
    c := make(chan string)
    var wg sync.WaitGroup

    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func() {
            sendString(c)
            wg.Done()
        }()
    }

    go func() {
        wg.Wait()
        close(c)
    }()

    for str := range c {
        fmt.Print(str)
    }

}
func sendString(ch chan string) {
    ch <- "a"
    ch <- "b"
    ch <- "c"
    ch <- "d"
}

https://play.golang.org/p/E_awt8UBK9v

【讨论】:

  • 谢谢!我也使用 WaitGroup 做了一个版本,但它给了我同样的错误,代码在上面,为什么会产生错误?使用 WaitGroup 时是否必须使用匿名函数?
  • 给通道一个大小,否则它将阻塞,直到从中读取到一些东西:play.golang.org/p/IxqyF42wwcv。更多信息在这里tour.golang.org/concurrency/3
  • 很明显,这需要知道有多少东西会预先通过通道发送,而在另一个例程中关闭通道则不需要
  • 非常感谢,我还有一个问题,在你给出的第一个例子中,它没有指定频道的大小,但是为什么频道可以接受四个字符串而没有任何接收功能?跨度>
  • 第二个 for 循环接收的范围。这在您的初始代码中不起作用,因为当通道从未关闭时,死锁发生在第二个 for 循环结束时。在初始等待组代码中它锁定在发送中。
猜你喜欢
  • 2017-02-23
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 2020-12-24
  • 2021-06-30
  • 2014-02-22
  • 2020-07-16
相关资源
最近更新 更多