go - 何时将 sync.Mutex 与 net/http 和 gorilla/mux 一起使用
问题描述
据我所知,net/http 包使用 goroutines 作为处理程序。sync.Mutex
为了防止函数中可能出现的错误nextId
导致函数可以计算地图的旧状态,是否有必要锁定地图?
这是我的示例代码:
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
}
解决方案
由于标准 lib 的 HTTP 服务器在其自己的 goroutine 上调用处理程序,因此您必须同步访问在处理程序之外定义的所有变量(其中一种访问是写入)。每当您使用 stdlib 的 HTTP 服务器时,您都必须这样做。无论您使用标准库的多路复用器还是 Gorilla 的多路复用器都没有关系。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
...
推荐阅读
- c# - 在子目录中搜索特定文件的最快方法
- selenium - 如何使用 Selenium 处理同一定位器的多个 xpath?
- javascript - 更新 localStorage 值(待办事项列表)
- unity3d - Unity 3d刚体移动但不是子对象
- azure - 使用 Powershell 的 Azure 成本警报
- python - 无论如何,我可以记录不规则阵列的形状吗?
- flutter - 如何在 Flutter 中使用 multi_image_picker 调整图像大小?
- python - OpenCv 形状检测到形状转换(Python)
- jquery - 有什么方法可以检查 slick 中的所有图像是否都已加载?
- android - 从 Azure Active Directory B2C Android/Java 中删除用户