【问题标题】:go routines deadlocked even if channel was closed即使通道关闭,执行例程也会陷入僵局
【发布时间】:2014-09-19 12:49:30
【问题描述】:

我有一个列表,有一个从中弹出元素的函数,以及另一个“接收”弹出元素的函数。我认为在接收器之后关闭通道会关闭通道,但似乎程序在到达那里之前是死锁的。这样做的最佳方法是什么?我是否应该有另一个通道来检测弹出何时完成?

Playground link

func pop(list *[]int, c chan int) {
    if len(*list) != 0 {
        result := (*list)[0]
        *list = (*list)[1:]
        fmt.Println("about to send ", result)
        c <- result
    } else {
        return
    }
}

func receiver(c chan int) {

    result := <-c
    fmt.Println("received ", result)
}

var list = []int{1, 2, 3}

func main() {

    fmt.Println("Main")
    c := make(chan int)
    go pop(&list, c)
    go pop(&list, c)
    for len(list) > 0 {
        receiver(c)
    }
    close(c) //Dosen't seem to have any effect
    fmt.Println("done")

}

【问题讨论】:

  • 请在代码上运行'go fmt' - 这样我们更容易阅读它。如果您发布一个完整的示例,我们甚至可以将其复制并粘贴到我们的编辑器中并使用它。
  • 对不起,上次有人编辑了我的代码,只留下了函数。我将添加一个指向操场的链接

标签: concurrency go goroutine


【解决方案1】:

代码有这么多问题,看看吧。

  1. 您的 pop 函数在访问切片时没有锁定,所以这就是数据竞争。
  2. for len(list) &gt; 0 {} 是一场数据竞赛,因为您正在访问列表,同时在其他 2 个 goroutine 中修改它。
  3. for len(list) &gt; 0 {} 永远不会返回,因为您的列表中有 3 项,但您只调用了两次 pop。
  4. receiver(c) 错误因为 #3,它尝试从通道读取,但没有写入任何内容。

一种方法是使用一个写入器 (pop) 和多个读取器 (receiver):

func pop(list *[]int, c chan int, done chan bool) {
    for len(*list) != 0 {
        result := (*list)[0]
        *list = (*list)[1:]
        fmt.Println("about to send ", result)
        c <- result
    }
    close(c)
    done <- true
}

func receiver(c chan int) {
    for result := range c {
        fmt.Println("received ", result)
    }
}

var list = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

func main() {
    c := make(chan int)
    done := make(chan bool)
    go pop(&list, c, done)
    go receiver(c)
    go receiver(c)
    go receiver(c)
    <-done
    fmt.Println("done")
}

playground

在处理 goroutine 时总是使用 go run -race blah.go

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2019-04-28
  • 2020-04-07
  • 1970-01-01
  • 2022-07-14
  • 1970-01-01
  • 2017-08-12
  • 1970-01-01
  • 2016-12-05
相关资源
最近更新 更多