如何在并发goroutine中锁定

如何在并发goroutine中锁定

本文介绍了如何在并发goroutine中锁定/同步对Go中变量的访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在他对这个问题的回答中:
user @distributed建议在同步的例程中锁定/同步对共享变量的访问。

我该怎么做?



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

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

$ p $ 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 ,r.URL.Path [1:],views)
views ++
}
}

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

 计数猴子,迄今为止。 
计数猴子,到目前为止5。
计数猴子,到目前为止5。
计数猴子,到目前为止8。
计数猴子,到目前为止8。
计数猴子,到目前为止8。
计数猴子,至今已有11只。

它可以很好地递增,但是当它被打印时,我可以看到打印+增量操作不是原子的



如果我将其更改为:

  func makeHomeHandler (c * http.Conn,r * http.Request){
views:= 0
return func(c * http.Conn,r * http.Request){
views ++
//我只能希望其他goroutine不会在此时增加计数器
//,即紧接在前一行之后和
//下一个执行之前!
views_now:= views
fmt.Fprintf(c,Counting%s,%d so far。,r.URL.Path [1:],views_now)
}
}

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

解决方案

如果一个同步计数器是你想要的,然后使用 sync.Mutex 是规范的解决方案。同步/原子包只能用于低级别的东西,或者当您测量出严重的性能问题时。

  type Counter struct {
mu sync.Mutex
x int64
}

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

func(c * Counter)Value()(x int64){
c.mu.Lock()
x = cx
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到目前为止。,r.URL.Path [1:],views.Value())
views.Add(1)
}
}

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

  type homeHandler struct {
mu sync.Mutex
views int64
}

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

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


In his answer to this question: Golang for Windows erratic behavior?user @distributed recommended to lock/synchronize access to a shared variable on concurrent goroutines.

How can I do that?

More on the issue:

I get this code (the returned function with a closure on views) running on several goroutines at the same time:

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++
    }
}

It looks like the IO function takes it's time, and as a result I get this kind of output:

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.

It increments fine, but when it gets printed I can see that the operation printing+incrementing is not atomic at all.

If I change it to:

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)
    }
}

It seems to work fine, but I'm not completely sure if it will not fail eventually...

解决方案

If a synchronized counter is all you want, then using sync.Mutex is the canonical solution. The sync/atomic package should only be used for low level stuff or when you've measured a serious performance problem.

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)
    }
}

For your particular problem, I'd suggest defining a new type that satisfies the http.Handler interface, rather than returning a closure. That looks simpler too:

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))
}

这篇关于如何在并发goroutine中锁定/同步对Go中变量的访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 06:14