使用共享地图的不错的,惯用的方式

Ale*_*lec 10 concurrency go

假设我有一个可以并发访问地图的程序,如下所示:

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()
})
Run Code Online (Sandbox Code Playgroud)

这很糟糕,因为地图写入是非原子的.所以我可以使用读/写互斥锁

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()
})
Run Code Online (Sandbox Code Playgroud)

这看起来很好,除了我们直接使用互斥体而不是通道.

实现这个的更实际的方法是什么?或者这是一个互联网真正需要的时代吗?

Ral*_*veo 11

我认为这个大小取决于你的表现期望以及最终如何使用这张地图.

当我研究同样的问题时,我遇到了这篇非常有用的文章,应该回答你的问题.

我个人的回答是,除非你真的发现需要使用互斥锁,否则你应该默认使用频道.惯用Go的核心点是,如果您坚持使用更高级别的通道功能,则不需要使用互斥锁并担心锁定.记住Go的座右铭:"通过沟通共享记忆,不要通过共享记忆来沟通."

还有一个细节,非常详细地介绍了在Mark Summerfield的Go书中建立一个安全地图以便同时使用的不同技术.

为了突出Rob Pike的幻灯片,Go的创造者之一:

并发简化了同步

  • 不需要显式同步
  • 程序的结构是隐式同步的

当你沿着使用像互斥体这样的原语的道路走下去时,由于你的程序更加复杂,这是非常非常难以做到的.你被警告了.

这里还引用了Golang网站本身:

由于实现对共享变量的正确访问所需的微妙之处,许多环境中的并发编程变得困难.Go鼓励采用一种不同的方法,在这种方法中,共享值在通道上传递,实际上,不会由单独的执行线程主动共享.在任何给定时间,只有一个goroutine可以访问该值.这种方法可以采取太多措施.例如,可以通过在整数变量周围放置互斥量来最好地完成引用计数.但作为一种高级方法,使用通道来控制访问可以更容易地编写清晰,正确的程序.

  • 您应该**从不** 使用“默认频道”。[引用](https://code.google.com/p/go-wiki/wiki/MutexOrChannel):“使用最具表现力和/或最简单的那个。去新手..过度使用频道..只是因为它是可能的。” 所以“使用最有表现力的那个” - 意思是*考虑*首先考虑它。 (2认同)

Nic*_*ood 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"))
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接