go - 如何在并发 goroutine 期间锁定/同步对 Go 中变量的访问?

标签 go goroutine

在他对这个问题的回答中:Golang for Windows erratic behavior? 用户@distributed 建议在并发 goroutine 上锁定/同步对共享变量的访问。

我该怎么做?

关于这个问题的更多信息:

我得到这段代码(返回的函数在 views 上有一个闭包)同时在多个 goroutine 上运行:

func makeHomeHandler() func(c *http.Conn, r *http.Request) {
    views := 1
    return func(c *http.Conn, r *http.Request) {
        fmt.Fprintf(c, "Counting %s, %d so far.", r.URL.Path[1:], views)
        views++
    }
}

看起来 IO 函数需要时间,结果我得到了这样的输出:

Counting monkeys, 5 so far.
Counting monkeys, 5 so far.
Counting monkeys, 5 so far.
Counting monkeys, 8 so far.
Counting monkeys, 8 so far.
Counting monkeys, 8 so far.
Counting monkeys, 11 so far.

它的增量很好,但是当它被打印出来时,我可以看到打印+增量操作根本不是原子的。

如果我将其更改为:

func makeHomeHandler() func(c *http.Conn, r *http.Request) {
    views := 0
    return func(c *http.Conn, r *http.Request) {
        views++
        // I can only hope that other goroutine does not increment the counter 
        // at this point, i.e., right after the previous line and before the 
        // next one are executed!
        views_now := views
        fmt.Fprintf(c, "Counting %s, %d so far.", r.URL.Path[1:], views_now)
    }
}

它似乎工作正常,但我不完全确定它是否最终不会失败......

最佳答案

如果您只需要同步计数器,那么使用 sync.Mutex 是规范的解决方案。 sync/atomic 包应该只用于低级别的东西或当您测量到严重的性能问题时。

type Counter struct {
    mu  sync.Mutex
    x   int64
}

func (c *Counter) Add(x int64) {
    c.mu.Lock()
    c.x += x
    c.mu.Unlock()
}

func (c *Counter) Value() (x int64) {
    c.mu.Lock()
    x = c.x
    c.mu.Unlock()
    return
}

func makeHomeHandler() func(c http.ResponseWriter, r *http.Request) {
    var views Counter
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], views.Value())
        views.Add(1)
    }
}

对于您的特定问题,我建议定义一个满足 http.Handler 接口(interface)的新类型,而不是返回一个闭包。这看起来也更简单:

type homeHandler struct {
    mu  sync.Mutex
    views   int64
}

func (h *homeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.mu.Lock()
    defer h.mu.Unlock()
    fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], h.views)
    h.views++
}

func init() {
    http.Handle("/", new(homeHandler))
}

关于go - 如何在并发 goroutine 期间锁定/同步对 Go 中变量的访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10728863/

相关文章:

go - 触发并忘记 goroutine golang

go - 避免goroutines之间双向通信的死锁

go - Golang: map 界面,如何打印键和值

go - 结构中的线程,函数参数对于新的 goroutine 来说太大

xml - GoLang : Decompress bz2 in on goroutine, 在其他协程中消费

go - golang从dll返回char *作为返回值

concurrency - 戈朗 : Producer/Consumer concurrency model but with serialized results

go - 为什么systemd无法启动golang web app,没有答案

go - 在 go lang 中,为什么这是 7 ?断言(5^2 == 7)

go - 为什么在 golang 中仅初始化定义中的一个变量会失败