I’m trying to implement an Observer Pattern suggested here; Observer pattern in Go language
(the code listed above doesn’t compile and is incomplete). Here, is a complete code that compiles but I get deadlock error.
package main
import (
"fmt"
)
type Publisher struct{
listeners []chan int
}
type Subscriber struct{
Channel chan int
Name string
}
func (p *Publisher) Sub(c chan int){
p.listeners = append(p.listeners, c)
}
func (p *Publisher) Pub(m int, quit chan int){
for _, c := range p.listeners{
c <- m
}
quit <- 0
}
func (s *Subscriber) ListenOnChannel(){
data := <-s.Channel
fmt.Printf("Name: %v; Data: %v\n", s.Name, data)
}
func main() {
quit := make(chan int)
p := &Publisher{}
subscribers := []*Subscriber{&Subscriber{Channel: make(chan int), Name: "1"}, &Subscriber{Channel: make(chan int), Name: "2"}, &Subscriber{Channel: make(chan int), Name: "3"}}
for _, v := range subscribers{
p.Sub(v.Channel)
go v.ListenOnChannel()
}
p.Pub(2, quit)
<-quit
}
Also, if I get rid of ‘quit’ completely, I get no error but it only prints first record.
The problem is that you’re sending to quit on the same goroutine that’s receiving from
quit.quithas a buffer size of 0, which means that in order to proceed there has to be a sender on one side and a receiver on the other at the same time. You’re sending, but no one’s on the other end, so you wait forever. In this particular case the Go runtime is able to detect the problem and panic.The reason only the first value is printed when you remove quit is that your main goroutine is exiting before your remaining two are able to print.
Do not just increase channel buffer sizes to get rid of problems like this. It can help (although in this case it doesn’t), but it only covers up the problem and doesn’t truly fix the underlying cause. Increasing a channel’s buffer size is strictly an optimization. In fact, it’s usually better to develop with no buffer because it makes concurrency problems more obvious.
There are two ways to fix the problem:
quit, but send 0 on it in each goroutine insideListenOnChannel. Inmain, make sure you receive a value from each goroutine before moving on. (In this case, you’ll wait for three values.)