【问题标题】:how to prevent race conditions when read-only a shared struct from an HTTP handler从 HTTP 处理程序只读共享结构时如何防止竞争条件
【发布时间】:2019-02-22 21:51:48
【问题描述】:

我需要从 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

【问题讨论】:

    标签: go


    【解决方案1】:

    你有几个选择:

    1. 使用互斥体,就像您现有的示例一样(尽管如果大多数访问都是读取,RWMutex 会更有效)。
    2. 将其包装在一个辅助函数中,这样您的处理程序就不必直接操作锁,只需调用该函数即可获取值,该函数将处理锁定/读取/解锁逻辑。
    3. 颠覆逻辑,让每个例程都有自己的值的本地副本,并使用通道通知例程权威值的变化(通过通信共享而不是通过共享进行通信)。

    【讨论】:

    • 我不确定它是否符合我的想法,但对于 boolint 值,atomic 不是一个选项吗?
    • 是的,但正如其文档所述,“这些功能需要非常小心才能正确使用。除了特殊的低级应用程序外,最好使用通道或同步包的工具来完成同步。 "
    • @Adrian 任何使用频道而不是 sync 包的示例?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 2017-07-26
    • 2016-03-21
    相关资源
    最近更新 更多