Когда использовать sync.Mutex с net / http и gorilla / mux - PullRequest
0 голосов
/ 08 ноября 2019

Насколько я знаю, пакет 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
}

1 Ответ

2 голосов
/ 08 ноября 2019

Поскольку HTTP-сервер стандартной библиотеки lib вызывает обработчики по своей собственной программе, вы должны синхронизировать доступ ко всем переменным, которые определены вне обработчиков (и где один из доступа является записью). Вы должны делать это всякий раз, когда используете HTTP-сервер stdlib. Не имеет значения, используете ли вы стандартный мультиплексор lib или Gorilla. Запуск программы происходит вне мультиплексора (до вызова мультиплексора).

Если этого не сделать (как в вашем примере), происходит гонка данных, которую вы можете проверить, запустив ее с опцией -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
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...