# #go
Вопрос:
Пытаясь очистить мой код, код GetDecisListeners()
ранее был внутри LiveDecision()
без панической ошибки.
//GetDecisListener return listener subscribed to channels
func GetDecisListener(dStructs map[string]map[string]interface{}) (chan interface{}, error) {
listener := make(chan interface{})
for r, _ := range dStructs {
DecisChannels[r].Register(listener)
key := r
defer func() {
Decision(key).Unregister(listener)
close(listener)
}()
fmt.Printf("Done %vn", r)
}
fmt.Print("Done insiden")
return listener, nil
}
func LiveDecision(ctx *gin.Context) {
...
listener, err := GetDecisListener(dStructs)
fmt.Print("Done outsiden") // cannot reach here
if err != nil {return}
ctx.Stream(func(w io.Writer) bool {
select {
case msg := <-listener:
return true
...
}}
}
dStructs
это просто карта 2D-интерфейса; Decision
и DecisChannels
на нее ссылаются отсюда.
var DecisChannels = make(map[string]broadcast.Broadcaster)
var DecisCols = make(map[string]string)
func OpenListener(decisid string) chan interface{} {
ite := make(chan interface{})
Decision(decisid).Register(ite)
return ite
}
func CloseListener(decisid string, ite chan interface{}) {
Decision(decisid).Unregister(ite)
close(ite)
}
func Decision(decisid string) broadcast.Broadcaster {
b, ok := DecisChannels[decisid]
if !ok {
b = broadcast.NewBroadcaster(10)
DecisChannels[decisid] = b
}
return b
}
Журнал показывает, что он не достиг Done outside
, но показывает http:panic
после GetDecisListener()
.
Done item1
Done item2
Done item3
Done item4
Done inside
2021/09/14 14:50:45 http: panic serving 127.0.0.1:39450: close of closed channel
goroutine 40 [running]:
net/http.(*conn).serve.func1(0xc0000c4280)
/usr/lib/go-1.13/src/net/http/server.go:1767 0x139
panic(0xd271c0, 0xf5c2b0)
/usr/lib/go-1.13/src/runtime/panic.go:679 0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8290, 0xe, 0xc0003660c0)
elsewhere/internal/app/route/client/client.go:42 0x68
panic(0xd271c0, 0xf5c2b0)
/usr/lib/go-1.13/src/runtime/panic.go:679 0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8300, 0xd, 0xc0003660c0)
elsewhere/internal/app/route/client/client.go:42 0x68
panic(0xd271c0, 0xf5c2b0)
/usr/lib/go-1.13/src/runtime/panic.go:679 0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8390, 0xd, 0xc0003660c0)
elsewhere/internal/app/route/client/client.go:42 0x68
somewhere/internal/app/route/client.GetDecisListener(0xc0001228a0, 0xc0003660c0, 0x0, 0x0)
elsewhere/internal/app/route/client/client.go:47 0x27f
somewhere/internal/app/route/client.LiveDecision(0xc0000f8200)
elsewhere/internal/app/route/client/client.go:77 0xb0
github.com/gin-gonic/gin.(*Context).Next(0xc0000f8200)
/home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go:165 0x3b
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000470000, 0xc0000f8200)
/home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go:489 0x614
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000470000, 0xf7cdc0, 0xc0001020e0, 0xc0000f8100)
/home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go:445 0x15d
net/http.serverHandler.ServeHTTP(0xc00046e0e0, 0xf7cdc0, 0xc0001020e0, 0xc0000f8100)
/usr/lib/go-1.13/src/net/http/server.go:2802 0xa4
net/http.(*conn).serve(0xc0000c4280, 0xf7ff00, 0xc00004a040)
/usr/lib/go-1.13/src/net/http/server.go:1890 0x875
created by net/http.(*Server).Serve
/usr/lib/go-1.13/src/net/http/server.go:2928 0x384
Я подозреваю defer func()
Decision(key)
, что это вызвало панику. Я бы понятия не имел, куда его девать, если это так.
Комментарии:
1. Вам нужно закрыть и отменить регистрацию вашего слушателя в другом месте, прямо сейчас
GetDecisListener
возвращается закрытый канал.2. о…я вижу….слушатели закрыты, как только он покинул GetDecisListener()…
Ответ №1:
Вы не можете закрыть канал дважды: это вызовет панику.
listener := make(chan interface{})
for r, _ := range dStructs {
DecisChannels[r].Register(listener)
key := r
defer func() {
Decision(key).Unregister(listener)
close(listener)
}()
fmt.Printf("Done %vn", r)
}
Здесь , если len(dStructs) > 1
два отложенных вызова попытаются закрыть listener
Может быть, вы сможете сделать что-то вроде:
listener := make(chan interface{})
shouldClose := false
for r, _ := range dStructs {
DecisChannels[r].Register(listener)
key := r
defer func() {
Decision(key).Unregister(listener)
}()
shouldClose = true
fmt.Printf("Done %vn", r)
}
if shouldClose {
close(listener)
}
Комментарии:
1. хорошо, спасибо, похоже, что отсрочка
close(listener)
должна быть добавлена после ее объявления. И нет смысла вытаскивать эти коды изLiveDecision()