英文:
Golang http: panic serving: close of closed channel
问题
尝试清理我的代码,GetDecisListeners()
函数中的代码以前是在 LiveDecision()
函数内部而没有恐慌错误的。
// GetDecisListener 返回订阅频道的监听器
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 %v\n", r)
}
fmt.Print("Done inside\n")
return listener, nil
}
func LiveDecision(ctx *gin.Context) {
...
listener, err := GetDecisListener(dStructs)
fmt.Print("Done outside\n") // 无法到达此处
if err != nil {return}
ctx.Stream(func(w io.Writer) bool {
select {
case msg := <-listener:
return true
...
}}
}
dStructs
只是一个二维接口映射;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
}
日志显示在 GetDecisListener()
之后没有到达 Done outside
,而是显示了 http:panic
错误。
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
我怀疑 Decision(key)
的 defer func()
导致了 panic 错误。如果是这种情况,我不知道应该把它放在哪里。
英文:
Trying to clean up my code, the code in GetDecisListeners()
was previously inside LiveDecision()
without panic error.
//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 %v\n", r)
}
fmt.Print("Done inside\n")
return listener, nil
}
func LiveDecision(ctx *gin.Context) {
...
listener, err := GetDecisListener(dStructs)
fmt.Print("Done outside\n") // cannot reach here
if err != nil {return}
ctx.Stream(func(w io.Writer) bool {
select {
case msg := <-listener:
return true
...
}}
}
dStructs
is just an 2D interface map; Decision
and DecisChannels
are referred from here.
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
}
The log shows it hadn't reach Done outside
but shows http:panic
after the 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
I suspect defer func()
of the Decision(key)
causing the panic. I would have no idea where to put it if this is the case.
答案1
得分: 1
你不能两次关闭一个通道,否则会引发 panic。
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 %v\n", 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 %v\n", r)
}
if shouldClose {
close(listener)
}
英文:
You cannot close a channel twice: it will panic.
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 %v\n", r)
}
Here if len(dStructs) > 1
, two defered call will try to close listener
Maybe you can do something like:
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 %v\n", r)
}
if shouldClose {
close(listener)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论