问题描述
在他对这个问题的回答中:
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中变量的访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!