我收到的以下go代码出了什么问题'所有goroutines都睡着了 - 死锁!'

ash*_*lal 1 channel go goroutine

我正在尝试实现这里建议的观察者模式; 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              
}
Run Code Online (Sandbox Code Playgroud)

此外,如果我完全摆脱'退出',我没有错误,但它只打印第一个记录.

Eva*_*haw 5

问题是你正在发送退出接收的同一个goroutine quit.

quit具有为0的缓冲器大小,这意味着为了进行,必须有在一侧上的发送器,而在另一接收机在同一时间.你正在发送,但没有人在另一端,所以你永远等待.在这种特殊情况下,Go运行时能够检测到问题和恐慌.

删除退出时仅打印第一个值的原因是您的主要goroutine在剩余的两个能够打印之前退出.

不要只是增加通道的缓冲区大小摆脱这样的问题.它可以提供帮助(虽然在这种情况下它没有),但它只能解决问题,并没有真正解决根本原因.增加通道的缓冲区大小绝对是一种优化.事实上,通常在没有缓冲区的情况下开发更好,因为它使并发问题更加明显.

有两种方法可以解决问题:

  • 保持quit,但在每个goroutine内部发送0 ListenOnChannel.在进行之前main,请确保从每个goroutine收到一个值.(在这种情况下,您将等待三个值.)
  • 使用WaitGroup.它有一个很好的例子,说明它在文档中是如何工作的.