From c7864d5dc8339c6065676250858151cada8a3bcc Mon Sep 17 00:00:00 2001 From: charlie <3140647@qq.com> Date: Thu, 12 May 2022 14:32:58 +0800 Subject: [PATCH] Synchronize --- maps/concurrence_test.go | 27 ++++++++++++++++++++++ maps/hash_map.go | 49 ++++++++++++++++++++++++++++++++++++---- maps/option.go | 5 ++-- 3 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 maps/concurrence_test.go diff --git a/maps/concurrence_test.go b/maps/concurrence_test.go new file mode 100644 index 0000000..d71777e --- /dev/null +++ b/maps/concurrence_test.go @@ -0,0 +1,27 @@ +package maps + +import ( + "sync" + "testing" + "time" +) + +func TestConcurrence(t *testing.T) { + m := NewHashMap(map[string]any{"a": "a"}).Synchronize() + + var wg = &sync.WaitGroup{} + for i := 0; i < 100; i++ { + wg.Add(1) + + go func() { + defer wg.Done() + + m.Set("b", "b") + m.Get("a") + + time.Sleep(time.Second) + }() + } + + wg.Wait() +} diff --git a/maps/hash_map.go b/maps/hash_map.go index 8385d11..2425a24 100644 --- a/maps/hash_map.go +++ b/maps/hash_map.go @@ -1,11 +1,13 @@ package maps import ( + "github.com/charlienet/go-mixed/locker" "golang.org/x/exp/constraints" ) type hashMap[K constraints.Ordered, V any] struct { - m map[K]V + m map[K]V + opt *options } func NewHashMap[K constraints.Ordered, V any](maps ...map[K]V) *hashMap[K, V] { @@ -14,28 +16,41 @@ func NewHashMap[K constraints.Ordered, V any](maps ...map[K]V) *hashMap[K, V] { m = Merge(maps...) } - return &hashMap[K, V]{m: m} + return &hashMap[K, V]{opt: acquireDefaultOptions(), m: m} } -// synchronized -func (m *hashMap[K, V]) Synchronized() *hashMap[K, V] { +// Synchronize +func (m *hashMap[K, V]) Synchronize() *hashMap[K, V] { + m.opt.mu = locker.NewRWLocker() + m.opt.hasLocker = true + return m } func (m *hashMap[K, V]) Set(key K, value V) { + m.opt.mu.Lock() m.m[key] = value + m.opt.mu.Unlock() } func (m *hashMap[K, V]) Get(key K) (V, bool) { + m.opt.mu.RLock() v, exist := m.m[key] + m.opt.mu.RUnlock() return v, exist } func (m *hashMap[K, V]) Delete(key K) { + m.opt.mu.Lock() + defer m.opt.mu.Unlock() + delete(m.m, key) } func (m *hashMap[K, V]) Exist(key K) bool { + m.opt.mu.RLock() + defer m.opt.mu.RUnlock() + _, ok := m.m[key] return ok } @@ -43,6 +58,9 @@ func (m *hashMap[K, V]) Exist(key K) bool { func (m *hashMap[K, V]) Iter() <-chan *Entry[K, V] { ch := make(chan *Entry[K, V], m.Count()) go func() { + m.opt.mu.RLock() + defer m.opt.mu.RUnlock() + for k, v := range m.m { ch <- &Entry[K, V]{ Key: k, @@ -57,12 +75,18 @@ func (m *hashMap[K, V]) Iter() <-chan *Entry[K, V] { } func (m *hashMap[K, V]) ForEach(f func(K, V)) { + m.opt.mu.RLock() + defer m.opt.mu.RUnlock() + for k, v := range m.m { f(k, v) } } func (m *hashMap[K, V]) Keys() []K { + m.opt.mu.RLock() + defer m.opt.mu.RUnlock() + keys := make([]K, 0, m.Count()) for k := range m.m { keys = append(keys, k) @@ -72,6 +96,9 @@ func (m *hashMap[K, V]) Keys() []K { } func (m *hashMap[K, V]) Values() []V { + m.opt.mu.RLock() + defer m.opt.mu.RUnlock() + values := make([]V, 0, m.Count()) for _, v := range m.m { values = append(values, v) @@ -81,6 +108,9 @@ func (m *hashMap[K, V]) Values() []V { } func (m *hashMap[K, V]) ToMap() map[K]V { + m.opt.mu.RLock() + defer m.opt.mu.RUnlock() + mm := make(map[K]V, m.Count()) for k, v := range m.m { mm[k] = v @@ -90,6 +120,9 @@ func (m *hashMap[K, V]) ToMap() map[K]V { } func (m *hashMap[K, V]) Clear() { + m.opt.mu.Lock() + defer m.opt.mu.Unlock() + m.m = make(map[K]V) } @@ -98,5 +131,11 @@ func (m *hashMap[K, V]) Count() int { } func (m *hashMap[K, V]) Clone() Map[K, V] { - return NewHashMap(m.ToMap()) + ret := NewHashMap(m.ToMap()) + + if m.opt.hasLocker { + ret = ret.Synchronize() + } + + return ret } diff --git a/maps/option.go b/maps/option.go index 206dac5..58af67c 100644 --- a/maps/option.go +++ b/maps/option.go @@ -1,13 +1,12 @@ package maps import ( - "sync" - "github.com/charlienet/go-mixed/locker" ) type options struct { - mu sync.Locker + hasLocker bool + mu locker.RWLocker } func acquireDefaultOptions() *options {