据我所知,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
...

09-09 23:57
查看更多