From e3475c6c073dd940704de6f791ece7ea210dfe31 Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Fri, 12 Jan 2024 12:13:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=BF=81=E7=A7=BB=20concurrent.Bal?= =?UTF-8?q?anceMap=20=E8=87=B3=20mappings.SyncMap=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B3=9B=E5=9E=8B=E5=87=BD=E6=95=B0=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/collection/mappings/sync_map.go | 221 ++++++++++++++++++++++++ utils/concurrent/balance_map.go | 220 ----------------------- utils/concurrent/balance_map_options.go | 11 -- utils/leaderboard/binary_search.go | 20 +-- 4 files changed, 231 insertions(+), 241 deletions(-) create mode 100644 utils/collection/mappings/sync_map.go delete mode 100644 utils/concurrent/balance_map.go delete mode 100644 utils/concurrent/balance_map_options.go diff --git a/utils/collection/mappings/sync_map.go b/utils/collection/mappings/sync_map.go new file mode 100644 index 0000000..6da93c0 --- /dev/null +++ b/utils/collection/mappings/sync_map.go @@ -0,0 +1,221 @@ +package mappings + +import ( + "encoding/json" + "github.com/kercylan98/minotaur/utils/collection" + "sync" +) + +// NewSyncMap 创建一个 SyncMap +func NewSyncMap[K comparable, V any](source ...map[K]V) *SyncMap[K, V] { + m := &SyncMap[K, V]{ + data: make(map[K]V), + } + if len(source) > 0 { + m.data = collection.MergeMaps(source...) + } + return m +} + +// SyncMap 是基于 sync.RWMutex 实现的线程安全的 map +// - 适用于要考虑并发读写但是并发读写的频率不高的情况 +type SyncMap[K comparable, V any] struct { + lock sync.RWMutex + data map[K]V + atom bool +} + +// Set 设置一个值 +func (sm *SyncMap[K, V]) Set(key K, value V) { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + sm.data[key] = value +} + +// Get 获取一个值 +func (sm *SyncMap[K, V]) Get(key K) V { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + return sm.data[key] +} + +// Atom 原子操作 +func (sm *SyncMap[K, V]) Atom(handle func(m map[K]V)) { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + handle(sm.data) +} + +// Exist 判断是否存在 +func (sm *SyncMap[K, V]) Exist(key K) bool { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + _, exist := sm.data[key] + return exist +} + +// GetExist 获取一个值并判断是否存在 +func (sm *SyncMap[K, V]) GetExist(key K) (V, bool) { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + value, exist := sm.data[key] + return value, exist +} + +// Delete 删除一个值 +func (sm *SyncMap[K, V]) Delete(key K) { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + delete(sm.data, key) +} + +// DeleteGet 删除一个值并返回 +func (sm *SyncMap[K, V]) DeleteGet(key K) V { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + v := sm.data[key] + delete(sm.data, key) + return v +} + +// DeleteGetExist 删除一个值并返回是否存在 +func (sm *SyncMap[K, V]) DeleteGetExist(key K) (V, bool) { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + v, exist := sm.data[key] + delete(sm.data, key) + return v, exist +} + +// DeleteExist 删除一个值并返回是否存在 +func (sm *SyncMap[K, V]) DeleteExist(key K) bool { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + if _, exist := sm.data[key]; !exist { + sm.lock.Unlock() + return exist + } + delete(sm.data, key) + return true +} + +// Clear 清空 +func (sm *SyncMap[K, V]) Clear() { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + for k := range sm.data { + delete(sm.data, k) + } +} + +// ClearHandle 清空并处理 +func (sm *SyncMap[K, V]) ClearHandle(handle func(key K, value V)) { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + for k, v := range sm.data { + handle(k, v) + delete(sm.data, k) + } +} + +// Range 遍历所有值,如果 handle 返回 true 则停止遍历 +func (sm *SyncMap[K, V]) Range(handle func(key K, value V) bool) { + if !sm.atom { + sm.lock.Lock() + defer sm.lock.Unlock() + } + for k, v := range sm.data { + key, value := k, v + if handle(key, value) { + break + } + } +} + +// Keys 获取所有的键 +func (sm *SyncMap[K, V]) Keys() []K { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + var s = make([]K, 0, len(sm.data)) + for k := range sm.data { + s = append(s, k) + } + return s +} + +// Slice 获取所有的值 +func (sm *SyncMap[K, V]) Slice() []V { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + var s = make([]V, 0, len(sm.data)) + for _, v := range sm.data { + s = append(s, v) + } + return s +} + +// Map 转换为普通 map +func (sm *SyncMap[K, V]) Map() map[K]V { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + var m = make(map[K]V) + for k, v := range sm.data { + m[k] = v + } + return m +} + +// Size 获取数量 +func (sm *SyncMap[K, V]) Size() int { + if !sm.atom { + sm.lock.RLock() + defer sm.lock.RUnlock() + } + return len(sm.data) +} + +func (sm *SyncMap[K, V]) MarshalJSON() ([]byte, error) { + m := sm.Map() + return json.Marshal(m) +} + +func (sm *SyncMap[K, V]) UnmarshalJSON(bytes []byte) error { + var m = make(map[K]V) + if !sm.atom { + sm.lock.Lock() + sm.lock.Unlock() + } + if err := json.Unmarshal(bytes, &m); err != nil { + return err + } + sm.data = m + return nil +} diff --git a/utils/concurrent/balance_map.go b/utils/concurrent/balance_map.go deleted file mode 100644 index 24b43f1..0000000 --- a/utils/concurrent/balance_map.go +++ /dev/null @@ -1,220 +0,0 @@ -package concurrent - -import ( - "encoding/json" - "sync" -) - -// NewBalanceMap 创建一个并发安全且性能在普通读写和并发读写的情况下较为平衡的字典数据结构 -func NewBalanceMap[Key comparable, value any](options ...BalanceMapOption[Key, value]) *BalanceMap[Key, value] { - m := &BalanceMap[Key, value]{ - data: make(map[Key]value), - } - for _, option := range options { - option(m) - } - return m -} - -// BalanceMap 并发安全且性能在普通读写和并发读写的情况下较为平衡的字典数据结构 -// - 适用于要考虑并发读写但是并发读写的频率不高的情况 -type BalanceMap[Key comparable, Value any] struct { - lock sync.RWMutex - data map[Key]Value - atom bool -} - -// Set 设置一个值 -func (slf *BalanceMap[Key, Value]) Set(key Key, value Value) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - slf.data[key] = value -} - -// Get 获取一个值 -func (slf *BalanceMap[Key, Value]) Get(key Key) Value { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - return slf.data[key] -} - -// Atom 原子操作 -func (slf *BalanceMap[Key, Value]) Atom(handle func(m map[Key]Value)) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - handle(slf.data) -} - -// Exist 判断是否存在 -func (slf *BalanceMap[Key, Value]) Exist(key Key) bool { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - _, exist := slf.data[key] - return exist -} - -// GetExist 获取一个值并判断是否存在 -func (slf *BalanceMap[Key, Value]) GetExist(key Key) (Value, bool) { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - value, exist := slf.data[key] - return value, exist -} - -// Delete 删除一个值 -func (slf *BalanceMap[Key, Value]) Delete(key Key) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - delete(slf.data, key) -} - -// DeleteGet 删除一个值并返回 -func (slf *BalanceMap[Key, Value]) DeleteGet(key Key) Value { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - v := slf.data[key] - delete(slf.data, key) - return v -} - -// DeleteGetExist 删除一个值并返回是否存在 -func (slf *BalanceMap[Key, Value]) DeleteGetExist(key Key) (Value, bool) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - v, exist := slf.data[key] - delete(slf.data, key) - return v, exist -} - -// DeleteExist 删除一个值并返回是否存在 -func (slf *BalanceMap[Key, Value]) DeleteExist(key Key) bool { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - if _, exist := slf.data[key]; !exist { - slf.lock.Unlock() - return exist - } - delete(slf.data, key) - return true -} - -// Clear 清空 -func (slf *BalanceMap[Key, Value]) Clear() { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - for k := range slf.data { - delete(slf.data, k) - } -} - -// ClearHandle 清空并处理 -func (slf *BalanceMap[Key, Value]) ClearHandle(handle func(key Key, value Value)) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - for k, v := range slf.data { - handle(k, v) - delete(slf.data, k) - } -} - -// Range 遍历所有值,如果 handle 返回 true 则停止遍历 -func (slf *BalanceMap[Key, Value]) Range(handle func(key Key, value Value) bool) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - for k, v := range slf.data { - key, value := k, v - if handle(key, value) { - break - } - } -} - -// Keys 获取所有的键 -func (slf *BalanceMap[Key, Value]) Keys() []Key { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - var s = make([]Key, 0, len(slf.data)) - for k := range slf.data { - s = append(s, k) - } - return s -} - -// Slice 获取所有的值 -func (slf *BalanceMap[Key, Value]) Slice() []Value { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - var s = make([]Value, 0, len(slf.data)) - for _, v := range slf.data { - s = append(s, v) - } - return s -} - -// Map 转换为普通 map -func (slf *BalanceMap[Key, Value]) Map() map[Key]Value { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - var m = make(map[Key]Value) - for k, v := range slf.data { - m[k] = v - } - return m -} - -// Size 获取数量 -func (slf *BalanceMap[Key, Value]) Size() int { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - return len(slf.data) -} - -func (slf *BalanceMap[Key, Value]) MarshalJSON() ([]byte, error) { - m := slf.Map() - return json.Marshal(m) -} - -func (slf *BalanceMap[Key, Value]) UnmarshalJSON(bytes []byte) error { - var m = make(map[Key]Value) - if !slf.atom { - slf.lock.Lock() - slf.lock.Unlock() - } - if err := json.Unmarshal(bytes, &m); err != nil { - return err - } - slf.data = m - return nil -} diff --git a/utils/concurrent/balance_map_options.go b/utils/concurrent/balance_map_options.go deleted file mode 100644 index f49dc6b..0000000 --- a/utils/concurrent/balance_map_options.go +++ /dev/null @@ -1,11 +0,0 @@ -package concurrent - -// BalanceMapOption BalanceMap 的选项 -type BalanceMapOption[Key comparable, Value any] func(m *BalanceMap[Key, Value]) - -// WithBalanceMapSource 通过传入的 map 初始化 -func WithBalanceMapSource[Key comparable, Value any](source map[Key]Value) BalanceMapOption[Key, Value] { - return func(m *BalanceMap[Key, Value]) { - m.data = source - } -} diff --git a/utils/leaderboard/binary_search.go b/utils/leaderboard/binary_search.go index dd7ddaa..8ec4d63 100644 --- a/utils/leaderboard/binary_search.go +++ b/utils/leaderboard/binary_search.go @@ -2,7 +2,7 @@ package leaderboard import ( "encoding/json" - "github.com/kercylan98/minotaur/utils/concurrent" + "github.com/kercylan98/minotaur/utils/collection/mappings" "github.com/kercylan98/minotaur/utils/generic" ) @@ -11,7 +11,7 @@ func NewBinarySearch[CompetitorID comparable, Score generic.Ordered](options ... leaderboard := &BinarySearch[CompetitorID, Score]{ binarySearchEvent: new(binarySearchEvent[CompetitorID, Score]), rankCount: 100, - competitors: concurrent.NewBalanceMap[CompetitorID, Score](), + competitors: mappings.NewSyncMap[CompetitorID, Score](), } for _, option := range options { option(leaderboard) @@ -23,7 +23,7 @@ type BinarySearch[CompetitorID comparable, Score generic.Ordered] struct { *binarySearchEvent[CompetitorID, Score] asc bool rankCount int - competitors *concurrent.BalanceMap[CompetitorID, Score] + competitors *mappings.SyncMap[CompetitorID, Score] scores []*scoreItem[CompetitorID, Score] // CompetitorID, Score rankChangeEventHandles []BinarySearchRankChangeEventHandle[CompetitorID, Score] @@ -264,11 +264,11 @@ func (slf *BinarySearch[CompetitorID, Score]) competitor(competitorId Competitor func (slf *BinarySearch[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error { var t struct { - Competitors *concurrent.BalanceMap[CompetitorID, Score] `json:"competitors,omitempty"` - Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` - Asc bool `json:"asc,omitempty"` + Competitors *mappings.SyncMap[CompetitorID, Score] `json:"competitors,omitempty"` + Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` + Asc bool `json:"asc,omitempty"` } - t.Competitors = concurrent.NewBalanceMap[CompetitorID, Score]() + t.Competitors = mappings.NewSyncMap[CompetitorID, Score]() if err := json.Unmarshal(bytes, &t); err != nil { return err } @@ -280,9 +280,9 @@ func (slf *BinarySearch[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error func (slf *BinarySearch[CompetitorID, Score]) MarshalJSON() ([]byte, error) { var t struct { - Competitors *concurrent.BalanceMap[CompetitorID, Score] `json:"competitors,omitempty"` - Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` - Asc bool `json:"asc,omitempty"` + Competitors *mappings.SyncMap[CompetitorID, Score] `json:"competitors,omitempty"` + Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` + Asc bool `json:"asc,omitempty"` } t.Competitors = slf.competitors t.Scores = slf.scores