经常使用map,平时用着也很爽,但是突然某天流量上来了,程序不知不觉就挂了,还不清楚是怎么回事,明明以前用着好好的呀。所以有些好习惯在刚开始就养成,比如断言检查,并发安全考虑等。

map纵然很好用,但也得谨慎。或许很多人还不知道,golang内建map其实并不是并发安全的,下面我自定义了一个结构体,赋其map属性,给其绑定Get,Set方法方便操作。请看下面代码:

// M
type M struct {
    Map    map[string]string
}

// Set ...
func (m *M) Set(key, value string) {
    m.Map[key] = value
}

// Get ...
func (m *M) Get(key string) string {
    return m.Map[key]
}

上面的代码中,给一个结构体赋予了一个map属性,且绑定了两个方法,进行读写操作。当你在写了一个test在单个goroutine中跑的时候或许没什么问题,甚至10个goroutine,20个都还正常。

// TestMap  ...
func TestMap(t *testing.T) {
    c := helper.M{Map: make(map[string]string)}
    wg := sync.WaitGroup{}
    for i := 0; i < 21; i   {
        wg.Add(1)
        go func(n int) {
            k, v := strconv.Itoa(n), strconv.Itoa(n)
            c.Set(k, v)
            t.Logf("k=:%v,v:=%v\n", k, c.Get(k))
            wg.Done()
        }(i)
    }
    wg.Wait()
    t.Log("ok finished.")
}

然而当你你再添加的时候,goroutine再增加的时候,会报下面的错,也就是map并发写入出错.

fatal error: concurrent map writes

goroutine 75 [running]:
runtime.throw(0x13b2011, 0x15)

需要说明一点的是,在Http请求时,我们通常对参数封装,encode,可能会调用golang自带的url.Values{},通过读源码可以发现也是线程不安全的。