据我所知,net / http包使用goroutines作为处理程序。是否有必要用sync.Mutex
锁定 map ,以防止nextId
函数中可能出现的错误,导致该函数可能计算出 map 的旧状态?
这是我的示例代码:
package main
import (
"net/http"
"github.com/gorilla/mux"
"io/ioutil"
"fmt"
)
var testData = map[int]string {
1: "foo",
2: "bar",
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/data", getData).Methods("GET")
r.HandleFunc("/data", addData).Methods("POST")
http.ListenAndServe(":3000", r)
}
func getData(writer http.ResponseWriter, request *http.Request) {
for k, v := range testData {
fmt.Fprintf(writer, "Key: %d\tValue: %v\n", k, v)
}
}
func addData(writer http.ResponseWriter, request *http.Request) {
if data, err := ioutil.ReadAll(request.Body); err == nil {
if len(data) == 0 {
writer.WriteHeader(http.StatusBadRequest)
return
}
id := nextId()
testData[id] = string(data)
url := request.URL.String()
writer.Header().Set("Location", fmt.Sprintf("%s", url))
writer.WriteHeader(http.StatusCreated)
} else {
writer.WriteHeader(http.StatusBadRequest)
}
}
func nextId() int {
id := 1
for k, _ := range testData {
if k >= id {
id = k + 1;
}
}
return id
}
最佳答案
由于标准库的HTTP服务器在其自己的goroutine上调用处理程序,因此您必须同步访问在处理程序外部定义的所有变量(访问之一是写操作)。每当您使用stdlib的HTTP服务器时,都必须这样做。使用标准库的多路复用器还是大猩猩的都无所谓。 goroutine启动发生在多路复用器外部(在调用多路复用器之前)。
不这样做(如您的示例),就会发生数据争用,您可以通过使用-race
选项运行它来进行验证:
WARNING: DATA RACE
Write at 0x00c000090c30 by goroutine 21:
runtime.mapassign_fast64()
/usr/local/go/src/runtime/map_fast64.go:92 +0x0
main.addData()
/home/icza/gows/src/play/play.go:47 +0x191
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:2007 +0x51
github.com/gorilla/mux.(*Router).ServeHTTP()
/home/icza/gows/pkg/mod/github.com/gorilla/mux@v1.7.3/mux.go:212 +0x13e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2802 +0xce
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1890 +0x837
Previous read at 0x00c000090c30 by goroutine 7:
runtime.mapiternext()
/usr/local/go/src/runtime/map.go:851 +0x0
main.getData()
/home/icza/gows/src/play/play.go:32 +0x194
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:2007 +0x51
...