用golang做项目有一段时间了,然而正式环境有时会遇到程序异常崩溃的问题。查看日志发现错误原因是:fatal error: concurrent map writes
详细错误日志如下:
fatal error: concurrent map writes
goroutine 9884217 [running]:
runtime.throw(0xb997f3, 0x15)
/website/xdelve/plug/go/src/runtime/panic.go:596 +0x95 fp=0xc4204a9c48 sp=0xc4204a9c28
runtime.mapassign(0xb03220, 0xc420325470, 0xc4204a9d18, 0xc)
/website/xdelve/plug/go/src/runtime/hashmap.go:499 +0x667 fp=0xc4204a9ce8 sp=0xc4204a9c48
net/textproto.MIMEHeader.Set(0xc420325470, 0xb91a44, 0xc, 0xb9b828, 0x18)
/website/xdelve/plug/go/src/net/textproto/header.go:22 +0x71 fp=0xc4204a9d38 sp=0xc4204a9ce8
net/http.Header.Set(0xc420325470, 0xb91a44, 0xc, 0xb9b828, 0x18)
/website/xdelve/plug/go/src/net/http/header.go:31 +0x53 fp=0xc4204a9d70 sp=0xc4204a9d38
treemonster/server.(*T_Response).SetHeader(0xc420325e90, 0xb91a44, 0xc, 0xb9b828, 0x18)
/website/other/golang/src/treemonster/server/response.go:41 +0x69 fp=0xc4204a9da8 sp=0xc4204a9d70
treemonster/server.(*T_Response).JSONSuccess(0xc420325e90, 0xaa2f00, 0xc4201ae1a8, 0xaa2f00, 0xc4201ae1a8)
/website/other/golang/src/treemonster/server/response.go:49 +0x61 fp=0xc4204a9de0 sp=0xc4204a9da8
_/website/other/golang/go/app/controller.(*ApiController).Proxy.func1(0xc4201ae0b0, 0xc4201ae040)
/website/other/golang/go/app/controller/Api.go:31 +0x3a8 fp=0xc4204a9f10 sp=0xc4204a9de0
treemonster/js.(*T_Promise).getnew.func2.1()
/website/other/golang/src/treemonster/js/Promise.go:102 +0x91 fp=0xc4204a9f50 sp=0xc4204a9f10
treemonster/js.Try(0xc42037a7b0, 0xc42037a7a8, 0x1, 0x1)
/website/other/golang/src/treemonster/js/Try.go:11 +0x61 fp=0xc4204a9f88 sp=0xc4204a9f50
treemonster/js.(*T_Promise).getnew.func2()
/website/other/golang/src/treemonster/js/Promise.go:103 +0x89 fp=0xc4204a9fe0 sp=0xc4204a9f88
runtime.goexit()
/website/xdelve/plug/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc4204a9fe8 sp=0xc4204a9fe0
created by treemonster/js.SetImmediate
/website/other/golang/src/treemonster/js/Timer.go:56 +0x33
......
concurrent map writes实际原因是多个线程同时操作了map类型的变量。
通过日志信息我确定了出错的代码为:
r.Raw.Header().Set(key, value)
这里的r.Raw
返回的是http.ResponseWriter
,Header()
即 http.Header。由于Set方法是官方代码的,因此我此前从未考虑过可能会出现线程安全的错误。但现在必须要确认其逻辑是否真的有问题。
我查看了其定义出的代码:
https://golang.org/src/net/http/header.go?s=349:380#L9
func (h Header) Set(key, value string) {
textproto.MIMEHeader(h).Set(key, value)
}
https://golang.org/src/net/textproto/header.go?s=657:699#L11
func (h MIMEHeader) Set(key, value string) {
h[CanonicalMIMEHeaderKey(key)] = []string{value}
}
结果上看到确实是直接写这个map的,没有加锁。Fuck。。。。
确认原因之后我很无奈,但只能自己做一个读写锁来处理,希望以后官方代码能修复这个问题吧。
相关文档
随便看看
畅言模块加载中