From 8af40f47333ddc58343f8c69e90006672fe26b1c Mon Sep 17 00:00:00 2001 From: PualrDwade <544493924@qq.com> Date: Sun, 25 Aug 2019 00:53:36 +0800 Subject: [PATCH 1/2] use rwmutex to handle concurency --- snapshot.go | 38 +++++++++++++++++++++----------------- snapshot_test.go | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/snapshot.go b/snapshot.go index 78831dd..d23d53f 100644 --- a/snapshot.go +++ b/snapshot.go @@ -16,7 +16,7 @@ const ( prefix = "_sc_" ) -var mutexList = make(map[string]*sync.Mutex) +var mutexList = make(map[string]*sync.RWMutex) // Collection describes a collection of key value pairs type Collection struct { @@ -28,7 +28,7 @@ type Collection struct { // New return a instance of collection func New(name string) (*Collection, error) { if len(name) <= 0 { - return &Collection{}, errors.New("Collection name can not be empty!") + return &Collection{}, errors.New("Collection name can not be empty") } //make file path correct dir := prefix + filepath.Clean(name) @@ -42,10 +42,11 @@ func New(name string) (*Collection, error) { //Put store a new key with value in the collection func (c *Collection) Put(key string, value interface{}) error { if len(key) <= 0 { - return errors.New("Key can not be empty!") + return errors.New("Key can not be empty") } path := filepath.Join(c.dir, key+extension) - m := c.getMutex(path) + // need get read lock and write lock + m := c.getPathMutex(path) m.Lock() defer m.Unlock() file, err := os.Create(path) @@ -60,14 +61,14 @@ func (c *Collection) Put(key string, value interface{}) error { //Get retrieve a value from collection by key func (c *Collection) Get(key string, value interface{}) error { if len(key) <= 0 { - return errors.New("Key can not be empty!") + return errors.New("Key can not be empty") } path := filepath.Join(c.dir, key+extension) - m := c.getMutex(path) - m.Lock() - defer m.Unlock() + m := c.getPathMutex(path) + m.RLock() + defer m.RUnlock() if !c.Has(key) { - return fmt.Errorf("Key %s does not exist!", key) + return fmt.Errorf("Key %s does not exist", key) } file, err := os.Open(path) defer file.Close() @@ -81,16 +82,16 @@ func (c *Collection) Get(key string, value interface{}) error { //Remove delete a key from collection func (c *Collection) Remove(key string) error { if len(key) <= 0 { - return errors.New("Key can not be empty!") + return errors.New("Key can not be empty") } path := filepath.Join(c.dir, key+extension) - m := c.getMutex(path) + m := c.getPathMutex(path) m.Lock() defer m.Unlock() if c.Has(key) { return os.Remove(path) } - return fmt.Errorf("Key %s does not exist!", key) + return fmt.Errorf("Key %s does not exist", key) } //Flush delete a collection with its value @@ -135,14 +136,17 @@ func (c *Collection) TotalItem() int { return len(list) } -//populate a package level mutex list +// populate a package level mutex list // with key of full path of an item -func (c *Collection) getMutex(path string) *sync.Mutex { - c.mutex.Lock() - defer c.mutex.Unlock() +func (c *Collection) getPathMutex(path string) *sync.RWMutex { m, ok := mutexList[path] if !ok { - m = &sync.Mutex{} + c.mutex.Lock() + defer c.mutex.Unlock() + if m == nil { + m = &sync.RWMutex{} + } + // todo 多线程写map是否有问题 mutexList[path] = m } return m diff --git a/snapshot_test.go b/snapshot_test.go index 138718a..b14b44e 100644 --- a/snapshot_test.go +++ b/snapshot_test.go @@ -1,6 +1,7 @@ package snapshot import ( + "sync" "testing" "time" ) @@ -76,43 +77,63 @@ func TestCollection_Flush(t *testing.T) { func BenchmarkCollection_Put(b *testing.B) { john := user{"John Doe", "john.doe@mail.com", 9898787, time.Now()} for n := 0; n < b.N; n++ { - userCollection.Put("john", &john) + go userCollection.Put("john", &john) } } func BenchmarkCollection_Get(b *testing.B) { john := user{} for n := 0; n < b.N; n++ { - userCollection.Get("john", &john) - } + go userCollection.Get("john", &john) + } +} + +func BenchmarkCollection_Concurence(b *testing.B) { + group := &sync.WaitGroup{} + group.Add(2) + go func() { + for i := 0; i < b.N; i++ { + john := user{"John Doe", "john.doe@mail.com", 9898787, time.Now()} + userCollection.Put("john", john) + } + group.Done() + }() + go func() { + for i := 0; i < b.N; i++ { + john := user{} + userCollection.Get("john", john) + } + group.Done() + }() + group.Wait() } func BenchmarkCollection_Has(b *testing.B) { for n := 0; n < b.N; n++ { - userCollection.Has("john") + go userCollection.Has("john") } } func BenchmarkCollection_List(b *testing.B) { for n := 0; n < b.N; n++ { - userCollection.List() + go userCollection.List() } } func BenchmarkCollection_TotalItem(b *testing.B) { for n := 0; n < b.N; n++ { - userCollection.TotalItem() + go userCollection.TotalItem() } } func BenchmarkCollection_Remove(b *testing.B) { for n := 0; n < b.N; n++ { - userCollection.Remove("john") + go userCollection.Remove("john") } } func BenchmarkCollection_Flush(b *testing.B) { for n := 0; n < b.N; n++ { - userCollection.Flush() + go userCollection.Flush() } } From 6398c00eaa195a06bb13e370bc2f5564a03d32b0 Mon Sep 17 00:00:00 2001 From: PualrDwade <544493924@qq.com> Date: Sun, 25 Aug 2019 09:37:45 +0800 Subject: [PATCH 2/2] rm comment --- snapshot.go | 1 - snapshot_test.go | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/snapshot.go b/snapshot.go index d23d53f..f7f66df 100644 --- a/snapshot.go +++ b/snapshot.go @@ -146,7 +146,6 @@ func (c *Collection) getPathMutex(path string) *sync.RWMutex { if m == nil { m = &sync.RWMutex{} } - // todo 多线程写map是否有问题 mutexList[path] = m } return m diff --git a/snapshot_test.go b/snapshot_test.go index b14b44e..8561e8b 100644 --- a/snapshot_test.go +++ b/snapshot_test.go @@ -88,7 +88,7 @@ func BenchmarkCollection_Get(b *testing.B) { } } -func BenchmarkCollection_Concurence(b *testing.B) { +func BenchmarkCollection_Concurency(b *testing.B) { group := &sync.WaitGroup{} group.Add(2) go func() { @@ -110,30 +110,30 @@ func BenchmarkCollection_Concurence(b *testing.B) { func BenchmarkCollection_Has(b *testing.B) { for n := 0; n < b.N; n++ { - go userCollection.Has("john") + userCollection.Has("john") } } func BenchmarkCollection_List(b *testing.B) { for n := 0; n < b.N; n++ { - go userCollection.List() + userCollection.List() } } func BenchmarkCollection_TotalItem(b *testing.B) { for n := 0; n < b.N; n++ { - go userCollection.TotalItem() + userCollection.TotalItem() } } func BenchmarkCollection_Remove(b *testing.B) { for n := 0; n < b.N; n++ { - go userCollection.Remove("john") + userCollection.Remove("john") } } func BenchmarkCollection_Flush(b *testing.B) { for n := 0; n < b.N; n++ { - go userCollection.Flush() + userCollection.Flush() } }