2014-09-19 52 views
0

我有一个列表,其中包含一个函数,它包含pop元素,另一个函数用于“接收”弹出的元素。我认为在接收机关闭后才能关闭该频道,但似乎该节目在到达之前就陷入僵局。这是做这件事的最好方法?我应该有另一个通道来检测流行音乐何时完成吗?即使通道已关闭,程序也会死锁

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") 

} 
+1

请在代码上运行'go fmt' - 这样我们就可以更轻松地阅读它。如果您发布了一个完整的工作示例,我们甚至可以将其复制并粘贴到我们的编辑器中并与之一起玩。 – topskip 2014-09-19 12:49:57

+0

对不起,上次有人编辑我的代码,只留下了功能。我会添加一个链接到操场 – meto 2014-09-19 14:46:15

回答

6

有这么多问题的代码,让我们来看看。

  1. 您的pop功能在访问切片时不锁定,所以这是一个数据竞争。
  2. for len(list) > 0 {}是一个数据竞赛,因为您正在访问列表,同时在其他两个goroutine中修改它。
  3. for len(list) > 0 {}永远不会返回,因为您的列表中有3个项目,但您只需调用两次弹出窗口。
  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

有够程搞乱时总是使用go run -race blah.go

+2

+1,虽然'-race'没有检测到所有东西(http://stackoverflow.com/a/20282328/6309) – VonC 2014-09-19 13:20:23

相关问题