как предотвратить состояние гонки, когда разделяемая структура доступна только для чтения, из обработчика HTTP - PullRequest
0 голосов
/ 18 сентября 2018

Мне нужно обновить значения из struct и вернуть (только для чтения) не запись из обработчика HTTP, чтобы избежать условий гонки, которые я использую sync.Mutex, это базовый пример:

http://play.golang.org/p/21IimsdKP6e

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "sync"
    "time"
)

type Counter struct {
    count uint
    flag  bool
    mu    sync.Mutex
    quit  chan struct{}
    time  time.Time
    wg    sync.WaitGroup
}

func (c *Counter) Start() {
    c.count = 1
    c.time = time.Now()
    c.flag = true
}

func (c *Counter) Listen() {
    srv := &http.Server{
        Addr:    ":8080",
        Handler: http.DefaultServeMux,
    }
    http.HandleFunc("/", c.HandleStatus)
    c.wg.Add(1)
    go func() {
        defer c.wg.Done()
        log.Println(srv.ListenAndServe())
    }()
    go func(quit chan struct{}) {
        <-quit
        if err := srv.Close(); err != nil {
            log.Printf("HTTP error: %v", err)
        }
    }(c.quit)
}

func (c *Counter) HandleStatus(w http.ResponseWriter, r *http.Request) {
    c.mu.Lock()
    defer c.mu.Unlock()
    status := struct {
        Count uint   `json:"count"`
        Flag  bool   `json:"flag"`
        Time  string `json:"time"`
    }{
        Count: c.count,
        Time:  c.time.UTC().Format(time.RFC3339),
        Flag:  c.flag,
    }
    w.Header().Set("Content-Type", "application/json")
    if err := json.NewEncoder(w).Encode(status); err != nil {
        log.Println(err)
    }

}

func main() {
    c := &Counter{
        quit: make(chan struct{}),
    }
    c.Start()
    c.Listen()
    timeout := time.After(time.Minute)
    for {
        select {
        case <-time.After(time.Second):
            c.mu.Lock()
            c.count += 1
            c.flag = !c.flag
            c.mu.Unlock()
        case <-timeout:
            close(c.quit)
            c.wg.Wait()
            return
        }
    }
}

У меня есть больше обработчиков, но я делаю там, где мне нужно читать из Counter struct Мне нужно добавить по каждому:

c.mu.Lock()
defer c.mu.Unlock()

И где переменные изменены, сделайте что-то вроде:

c.mu.Lock()
c.count += 1
c.flag = !c.flag
c.mu.Unlock()

Поэтому интересно, как мне лучше синхронизировать / упорядочить код, чтобы предотвратить добавление на каждый обработчик Lock/Unlock

1 Ответ

0 голосов
/ 18 сентября 2018

У вас есть несколько вариантов:

  1. Используйте мьютекс, как в вашем существующем примере (хотя RWMutex будет более эффективным, если большинство обращений будет считано).
  2. Оберните то же самое во вспомогательную функцию, так что вместо того, чтобы управлять блокировкой напрямую, ваши обработчики могут просто вызвать функцию, чтобы получить значение, и функция будет обрабатывать логику блокировки / чтения / разблокировки.
  3. Переверните логику с ног на голову, предоставив каждой подпрограмме собственную локальную копию значения и используя канал для уведомления подпрограмм об изменениях в авторитетном значении (совместное использование путем обмена данными вместо обмена путем обмена).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...