1

我正在尝试实现此处建议的观察者模式;Go语言中的观察者模式

(上面列出的代码无法编译且不完整)。在这里,是一个完整的编译代码,但我得到了死锁错误。

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              
}

此外,如果我完全摆脱“退出”,我不会收到任何错误,但它只会打印第一条记录。

4

2 回答 2

5

问题是你发送退出的同一个 goroutine 来自quit.

quit has 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:

  • Keep quit, but send 0 on it in each goroutine inside ListenOnChannel. In main, make sure you receive a value from each goroutine before moving on. (In this case, you'll wait for three values.)
  • Use a WaitGroup. There's a good example of how it works in the documentation.
于 2011-11-09T07:51:11.167 回答
2

总的来说,这看起来不错,但有一个问题。请记住,通道要么是缓冲的,要么是无缓冲的(同步的或异步的)。当您发送到无缓冲通道具有完整缓冲区的通道时,发送方将阻塞,直到接收方从通道中删除数据。

因此,我会问我自己的一两个问题:

  1. 退出通道是同步的还是异步的?
  2. 当执行命中时,Pub 中会发生什么quit<-0

解决您的问题并允许代码运行的一种解决方案是将倒数第二个代码行更改为go p.Pub(2, quit). 但还有另一种解决方案。你能看出它是什么吗?

如果我<-quit从原始代码中删除,我实际上并没有得到与您相同的行为。这不应该影响输出,因为正如它所写的那样,该行永远不会执行。

于 2011-11-09T07:35:45.933 回答