github.com/easierway/concurrent_map@v1.0.0/concurrent_map.go (about) 1 package concurrent_map 2 3 import ( 4 "sync" 5 ) 6 7 // ConcurrentMap is a thread safe map collection with better performance. 8 // The backend map entries are separated into the different partitions. 9 // Threads can access the different partitions safely without lock. 10 type ConcurrentMap struct { 11 partitions []*innerMap 12 numOfBlockets int 13 } 14 15 // Partitionable is the interface which should be implemented by key type. 16 // It is to define how to partition the entries. 17 type Partitionable interface { 18 // Value is raw value of the key 19 Value() interface{} 20 21 // PartitionKey is used for getting the partition to store the entry with the key. 22 // E.g. the key's hash could be used as its PartitionKey 23 // The partition for the key is partitions[(PartitionKey % m.numOfBlockets)] 24 // 25 // 1 Why not provide the default hash function for partition? 26 // Ans: As you known, the partition solution would impact the performance significantly. 27 // The proper partition solution balances the access to the different partitions and 28 // avoid of the hot partition. The access mode highly relates to your business. 29 // So, the better partition solution would just be designed according to your business. 30 PartitionKey() int64 31 } 32 33 type innerMap struct { 34 m map[interface{}]interface{} 35 lock sync.RWMutex 36 } 37 38 func createInnerMap() *innerMap { 39 return &innerMap{ 40 m: make(map[interface{}]interface{}), 41 } 42 } 43 44 func (im *innerMap) get(key Partitionable) (interface{}, bool) { 45 keyVal := key.Value() 46 im.lock.RLock() 47 v, ok := im.m[keyVal] 48 im.lock.RUnlock() 49 return v, ok 50 } 51 52 func (im *innerMap) set(key Partitionable, v interface{}) { 53 keyVal := key.Value() 54 im.lock.Lock() 55 im.m[keyVal] = v 56 im.lock.Unlock() 57 } 58 59 func (im *innerMap) del(key Partitionable) { 60 keyVal := key.Value() 61 im.lock.Lock() 62 delete(im.m, keyVal) 63 im.lock.Unlock() 64 } 65 66 // CreateConcurrentMap is to create a ConcurrentMap with the setting number of the partitions 67 func CreateConcurrentMap(numOfPartitions int) *ConcurrentMap { 68 var partitions []*innerMap 69 for i := 0; i < numOfPartitions; i++ { 70 partitions = append(partitions, createInnerMap()) 71 } 72 return &ConcurrentMap{partitions, numOfPartitions} 73 } 74 75 func (m *ConcurrentMap) getPartition(key Partitionable) *innerMap { 76 partitionID := key.PartitionKey() % int64(m.numOfBlockets) 77 return (*innerMap)(m.partitions[partitionID]) 78 } 79 80 // Get is to get the value by the key 81 func (m *ConcurrentMap) Get(key Partitionable) (interface{}, bool) { 82 return m.getPartition(key).get(key) 83 } 84 85 // Set is to store the KV entry to the map 86 func (m *ConcurrentMap) Set(key Partitionable, v interface{}) { 87 im := m.getPartition(key) 88 im.set(key, v) 89 } 90 91 // Del is to delete the entries by the key 92 func (m *ConcurrentMap) Del(key Partitionable) { 93 im := m.getPartition(key) 94 im.del(key) 95 }