假设我有一个可以同时访问地图的程序,如下所示:
func getKey(r *http.Request) string { ... }
values := make(map[string]int)
http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
fmt.Fprint(w, values[key])
})
http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
values[key] = rand.Int()
})
这很糟糕,因为地图写入是非原子的。所以我可以使用读/写互斥锁
func getKey(r *http.Request) string { ... }
values := make(map[string]int)
var lock sync.RWMutex
http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
lock.RLock()
fmt.Fprint(w, values[key])
lock.RUnlock()
})
http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
lock.Lock()
values[key] = rand.Int()
lock.Unlock()
})
除了我们直接使用互斥体而不是频道这一事实外,这似乎很好。
实现这一目标的更实际的方法是什么?或者这是一个互联网真正需要的时代吗?
答案 0 :(得分:11)
我认为这个大小取决于你的表现期望以及最终如何使用这张地图。
当我研究同样的问题时,我发现了这个非常有帮助的article that should answer your question。
我个人的回答是,除非您确实需要使用互斥锁,否则您应该默认使用频道。惯用Go的核心点是,如果您坚持使用更高级别的通道功能,则不需要使用互斥锁并担心锁定。记住Go的座右铭:“通过沟通来共享记忆,不要通过共享记忆来沟通。”
还有一个非常详细的介绍,在Mark Summerfield的Go book中建立一个安全地图的同时使用的不同技术。
要突出显示Rob Pike's slide,Go的创建者之一:
并发简化了同步
- 无需显式同步
- 程序的结构是隐式同步的
当您沿着使用像互斥体这样的原语的路径时,由于您的程序更复杂,因此非常,非常很难做到正确。你被警告了。
这里还引用了Golang site itself:
许多环境中的并发编程很难实现 实现对共享变量的正确访问所需的微妙之处。 Go鼓励采用不同的方法来传递共享值 在渠道上,实际上,从不主动分享 执行的线程。只有一个goroutine可以访问该值 任何给定的时间。这种方法可以采取太多措施。参考计数 可能最好通过在整数变量周围放置一个互斥量来实现 实例。但作为一种高级方法,使用渠道来控制 访问可以更容易地编写清晰,正确的程序。
答案 1 :(得分:7)
我会说互斥对这个应用程序没问题。将它们包裹在一个类型中,这样你就可以像以后一样改变主意。请注意嵌入sync.RWMutex
然后使锁定整齐。
type thing struct {
sync.RWMutex
values map[string]int
}
func newThing() *thing {
return &thing{
values: make(map[string]int),
}
}
func (t *thing) Get(key string) int {
t.RLock()
defer t.RUnlock()
return t.values[key]
}
func (t *thing) Put(key string, value int) {
t.Lock()
defer t.Unlock()
t.values[key] = value
}
func main() {
t := newThing()
t.Put("hello", 1)
t.Put("sausage", 2)
fmt.Println(t.Get("hello"))
fmt.Println(t.Get("potato"))
}
答案 2 :(得分:3)
您不能将锁本身用于消息队列。这就是渠道的用途。
您可以按频道模拟锁定,但这不是通道的用途。
使用锁可以并发安全地访问共享资源。
使用频道进行并发安全消息排队。
使用RWMutex保护地图写入。
答案 3 :(得分:0)
这是一种替代的基于渠道的方法,使用渠道作为互斥机制:
func getKey(r *http.Request) string { ... }
values_ch := make(chan map[string]int, 1)
values_ch <- make(map[string]int)
http.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
values := <- values_ch
fmt.Fprint(w, values[key])
values_ch <- values
})
http.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
key := getKey(r)
values := <- values_ch
values[key] = rand.Int()
values_ch <- values
})
我们最初将资源放在共享频道中。然后goroutines可以借用并返回该共享资源。但是,与RWMutex
的解决方案不同,多个读者可以相互阻止。