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.