github.com/qxnw/lib4go@v0.0.0-20180426074627-c80c7e84b925/concurrent/cmap/concurrent_map.go (about) 1 package cmap 2 3 import ( 4 "encoding/json" 5 "sync" 6 ) 7 8 //ConcurrentMap 线程安全的MAP,创建SHARD_COUNT个共享map(ConcurrentMapShared), 防止单个锁的瓶颈引用的性能问题 9 type ConcurrentMap []*ConcurrentMapShared 10 11 //ConcurrentMapShared 线程安全的共享map 12 type ConcurrentMapShared struct { 13 items map[string]interface{} 14 sync.RWMutex 15 } 16 17 // New 创建ConcurrentMap,线程安全map 18 func New(count int) ConcurrentMap { 19 m := make(ConcurrentMap, count) 20 for i := 0; i < count; i++ { 21 m[i] = &ConcurrentMapShared{items: make(map[string]interface{})} 22 } 23 return m 24 } 25 26 //GetShard 根据KEY获取共享map 27 func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared { 28 return m[uint(fnv32(key))%uint(len(m))] 29 } 30 31 //MSet 根据map设置值 32 func (m ConcurrentMap) MSet(data map[string]interface{}) { 33 for key, value := range data { 34 shard := m.GetShard(key) 35 shard.Lock() 36 shard.items[key] = value 37 shard.Unlock() 38 } 39 } 40 41 //Set 设置指定key的值,如果存在则覆盖 42 func (m *ConcurrentMap) Set(key string, value interface{}) { 43 // Get map shard. 44 shard := m.GetShard(key) 45 shard.Lock() 46 shard.items[key] = value 47 shard.Unlock() 48 } 49 50 //UpsertCb Callback to return new element to be inserted into the map 51 // It is called while lock is held, therefore it MUST NOT 52 // try to access other keys in same map, as it can lead to deadlock since 53 // Go sync.RWLock is not reentrant 54 type UpsertCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{} 55 56 //Upsert Insert or Update - updates existing element or inserts a new one using UpsertCb 57 func (m *ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res interface{}) { 58 shard := m.GetShard(key) 59 shard.Lock() 60 v, ok := shard.items[key] 61 res = cb(ok, v, value) 62 shard.items[key] = res 63 shard.Unlock() 64 return res 65 } 66 67 // SetIfAbsent Sets the given value under the specified key if no value was associated with it. 68 func (m *ConcurrentMap) SetIfAbsent(key string, value interface{}) (bool, interface{}) { 69 // Get map shard. 70 shard := m.GetShard(key) 71 shard.Lock() 72 v, ok := shard.items[key] 73 if !ok { 74 v = value 75 shard.items[key] = v 76 } 77 shard.Unlock() 78 return !ok, v 79 } 80 81 //SetCb 设置回调函数 82 type SetCb func(input ...interface{}) (interface{}, error) 83 84 //SetIfAbsentCb 值不存在是调用回调函数生成对象并添加到ConcurrentMap 中 85 func (m *ConcurrentMap) SetIfAbsentCb(key string, cb SetCb, input ...interface{}) (ok bool, v interface{}, err error) { 86 shard := m.GetShard(key) 87 shard.Lock() 88 v, ok = shard.items[key] 89 if !ok { 90 v, err = cb(input...) 91 if err != nil { 92 shard.Unlock() 93 return false, nil, err 94 } 95 shard.items[key] = v 96 } 97 shard.Unlock() 98 return !ok, v, err 99 } 100 101 //Get Retrieves an element from map under given key. 102 func (m ConcurrentMap) Get(key string) (interface{}, bool) { 103 // Get shard 104 shard := m.GetShard(key) 105 shard.RLock() 106 // Get item from shard. 107 val, ok := shard.items[key] 108 shard.RUnlock() 109 return val, ok 110 } 111 112 //Count Returns the number of elements within the map. 113 func (m ConcurrentMap) Count() int { 114 count := 0 115 rcount := len(m) 116 for i := 0; i < rcount; i++ { 117 shard := m[i] 118 shard.RLock() 119 count += len(shard.items) 120 shard.RUnlock() 121 } 122 return count 123 } 124 125 //Has Looks up an item under specified key 126 func (m *ConcurrentMap) Has(key string) bool { 127 // Get shard 128 shard := m.GetShard(key) 129 shard.RLock() 130 // See if element is within shard. 131 _, ok := shard.items[key] 132 shard.RUnlock() 133 return ok 134 } 135 136 //Remove Removes an element from the map. 137 func (m *ConcurrentMap) Remove(key string) { 138 // Try to get shard. 139 shard := m.GetShard(key) 140 shard.Lock() 141 delete(shard.items, key) 142 shard.Unlock() 143 } 144 145 //Pop Removes an element from the map and returns it 146 func (m *ConcurrentMap) Pop(key string) (v interface{}, exists bool) { 147 // Try to get shard. 148 shard := m.GetShard(key) 149 shard.Lock() 150 v, exists = shard.items[key] 151 delete(shard.items, key) 152 shard.Unlock() 153 return v, exists 154 } 155 156 //PopAll Removes an element from the map and returns it 157 func (m ConcurrentMap) PopAll() (v map[string]interface{}) { 158 v = make(map[string]interface{}) 159 count := len(m) 160 ch := make(chan Tuple) 161 go func() { 162 wg := sync.WaitGroup{} 163 wg.Add(count) 164 // Foreach shard. 165 for _, shard := range m { 166 go func(shard *ConcurrentMapShared) { 167 // Foreach key, value pair. 168 shard.RLock() 169 for key, val := range shard.items { 170 ch <- Tuple{key, val} 171 delete(shard.items, key) 172 } 173 shard.RUnlock() 174 wg.Done() 175 }(shard) 176 } 177 wg.Wait() 178 close(ch) 179 }() 180 START: 181 for { 182 select { 183 case tup, ok := <-ch: 184 if !ok { 185 break START 186 } 187 v[tup.Key] = tup.Val 188 } 189 } 190 return 191 } 192 193 //IsEmpty Checks if map is empty. 194 func (m *ConcurrentMap) IsEmpty() bool { 195 return m.Count() == 0 196 } 197 198 //Tuple Used by the Iter & IterBuffered functions to wrap two variables together over a channel, 199 type Tuple struct { 200 Key string 201 Val interface{} 202 } 203 204 //Iter Returns an iterator which could be used in a for range loop. 205 // 206 // Deprecated: using IterBuffered() will get a better performence 207 func (m ConcurrentMap) Iter() <-chan Tuple { 208 ch := make(chan Tuple) 209 count := len(m) 210 go func() { 211 wg := sync.WaitGroup{} 212 wg.Add(count) 213 // Foreach shard. 214 for _, shard := range m { 215 go func(shard *ConcurrentMapShared) { 216 // Foreach key, value pair. 217 shard.RLock() 218 for key, val := range shard.items { 219 ch <- Tuple{key, val} 220 } 221 shard.RUnlock() 222 wg.Done() 223 }(shard) 224 } 225 wg.Wait() 226 close(ch) 227 }() 228 return ch 229 } 230 231 //IterBuffered Returns a buffered iterator which could be used in a for range loop. 232 func (m ConcurrentMap) IterBuffered() <-chan Tuple { 233 ch := make(chan Tuple, m.Count()) 234 count := len(m) 235 go func() { 236 wg := sync.WaitGroup{} 237 wg.Add(count) 238 // Foreach shard. 239 for _, shard := range m { 240 go func(shard *ConcurrentMapShared) { 241 // Foreach key, value pair. 242 shard.RLock() 243 for key, val := range shard.items { 244 ch <- Tuple{key, val} 245 } 246 shard.RUnlock() 247 wg.Done() 248 }(shard) 249 } 250 wg.Wait() 251 close(ch) 252 }() 253 return ch 254 } 255 256 //Items Returns all items as map[string]interface{} 257 func (m ConcurrentMap) Items() map[string]interface{} { 258 tmp := make(map[string]interface{}) 259 260 // Insert items to temporary map. 261 for item := range m.IterBuffered() { 262 tmp[item.Key] = item.Val 263 } 264 265 return tmp 266 } 267 268 //Clear 删除所有元素 269 func (m ConcurrentMap) Clear() { 270 wg := sync.WaitGroup{} 271 count := len(m) 272 wg.Add(count) 273 // Foreach shard. 274 for _, shard := range m { 275 go func(shard *ConcurrentMapShared) { 276 // Foreach key, value pair. 277 shard.RLock() 278 for key := range shard.items { 279 delete(shard.items, key) 280 } 281 shard.RUnlock() 282 wg.Done() 283 }(shard) 284 } 285 wg.Wait() 286 } 287 288 //IterCb Iterator callback,called for every key,value found in 289 // maps. RLock is held for all calls for a given shard 290 // therefore callback sess consistent view of a shard, 291 // but not across the shards 292 type IterCb func(key string, v interface{}) bool 293 294 //RemoveCb Iterator callback,返回true从列表中移除key 295 type RemoveCb func(key string, v interface{}) bool 296 297 // Callback based iterator, cheapest way to read 298 // all elements in a map. 299 func (m *ConcurrentMap) IterCb(fn IterCb) { 300 b := false 301 for idx := range *m { 302 shard := (*m)[idx] 303 shard.RLock() 304 for key, value := range shard.items { 305 if !fn(key, value) { 306 b = true 307 break 308 } 309 } 310 shard.RUnlock() 311 if b { 312 break 313 } 314 } 315 } 316 317 //RemoveIterCb 循环移除 318 func (m *ConcurrentMap) RemoveIterCb(fn RemoveCb) int { 319 count := 0 320 for idx := range *m { 321 shard := (*m)[idx] 322 shard.RLock() 323 for key, value := range shard.items { 324 if fn(key, value) { 325 delete(shard.items, key) 326 count++ 327 } 328 329 } 330 shard.RUnlock() 331 } 332 return count 333 } 334 335 //Keys Return all keys as []string 336 func (m ConcurrentMap) Keys() []string { 337 count := m.Count() 338 ch := make(chan string, count) 339 go func() { 340 // Foreach shard. 341 wg := sync.WaitGroup{} 342 wg.Add(count) 343 for _, shard := range m { 344 go func(shard *ConcurrentMapShared) { 345 // Foreach key, value pair. 346 shard.RLock() 347 for key := range shard.items { 348 ch <- key 349 } 350 shard.RUnlock() 351 wg.Done() 352 }(shard) 353 } 354 wg.Wait() 355 close(ch) 356 }() 357 358 // Generate keys 359 keys := make([]string, count) 360 for i := 0; i < count; i++ { 361 keys[i] = <-ch 362 } 363 return keys 364 } 365 366 //MarshalJSON Reviles ConcurrentMap "private" variables to json marshal. 367 func (m ConcurrentMap) MarshalJSON() ([]byte, error) { 368 // Create a temporary map, which will hold all item spread across shards. 369 tmp := make(map[string]interface{}) 370 371 // Insert items to temporary map. 372 for item := range m.IterBuffered() { 373 tmp[item.Key] = item.Val 374 } 375 return json.Marshal(tmp) 376 } 377 378 func fnv32(key string) uint32 { 379 hash := uint32(2166136261) 380 const prime32 = uint32(16777619) 381 for i := 0; i < len(key); i++ { 382 hash *= prime32 383 hash ^= uint32(key[i]) 384 } 385 return hash 386 } 387 388 // Concurrent map uses Interface{} as its value, therefor JSON Unmarshal 389 // will probably won't know which to type to unmarshal into, in such case 390 // we'll end up with a value of type map[string]interface{}, In most cases this isn't 391 // out value type, this is why we've decided to remove this functionality.