何时将sync.Mutex与net/http和gorilla/mux一起使用

据我所知, net/http 包使用 goroutine 作为处理程序。我是否有必要锁定地图,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

}


DIEA
浏览 109回答 1
1回答

墨色风雨

由于标准库的 HTTP 服务器在其自己的 goroutine 上调用处理程序,因此您必须同步访问处理程序外部定义的所有变量(其中访问之一是写入)。每当您使用 stdlib 的 HTTP 服务器时都必须执行此操作。使用标准库的多路复用器还是 Gorilla 的多路复用器并不重要。Goroutine 启动发生在多路复用器外部(在调用多路复用器之前)。如果不这样做(如您的示例中所示),则会发生数据争用,您可以通过使用以下选项运行它来验证-race:WARNING: DATA RACEWrite 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 +0x837Previous 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...
打开App,查看更多内容
随时随地看视频慕课网APP