Reputation: 3719
I have a list, with a function that pop element from it, and another function that "receives" the popped elements. I thought that putting a close after the receiver would close the channel, but it seems that the program is deadlock before getting there. Which is the best way of doing this? Should I have another channel that detects when the pop are done?
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")
}
Upvotes: 0
Views: 73
Reputation: 99215
There are so many problems with the code, let's see.
pop
function doesn't lock when accessing the slice, so that's a data race right there.for len(list) > 0 {}
is a data race because you're accessing list while modifying it in 2 other goroutines.for len(list) > 0 {}
will never return because you have 3 items in your list but you call pop only twice.receiver(c)
errors because of #3, it tries to read from the channel but there's nothing writing to it.One way to do it is to use one writer (pop
) and multiple readers (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")
}
Always use go run -race blah.go
when messing with goroutines.
Upvotes: 6