map不是goroutine safe的,在多goroutine并发时需要上锁。
不上锁会出现什么问题呢?
资源竞争:会出现脏读,丢失更新等一系列状况。如读到的是旧数据或是读出来的对象已经被删除了,引发panic。
锁用的不对会发生什么呢?
- 第一种情况:死锁(dead lock),卡住。使用时lock了,但没有unlock,就锁死了。另外,注册copy对象里带锁时,注意是不是带锁状态一起拷贝了,这个也会死锁。
- 第二种情况:低效。锁对象时进行了耗时操作,导致了长时间的等待。代码要尽量减少锁持有的时间。
怎样才是上锁正确的姿势呢?
总结如下:(按效率从高到低排序)
- 无锁:无招胜有招。避免使用锁是最高效的。在不会引发资源竞争的地方就不要用锁。或者通过设计,将业务放在同一个goroutine下执行。
- concurrent-map
分段锁: 其中对key进行分段,一个段内使用一个锁,这样操作不同的key时,避免锁的阻塞开销,提高了效率。但是增加了内存消耗。 - sync.Map
官方实现,内部使用了读写两张表。在读出写少效率比较高,但写多时推荐使用分段锁。 - 给map加读写锁
在轻量级,使用不是很频繁时使用。
没有一种锁适合所有场合下使用,实际的使用按需求的不同。