Skip to content

Commit

Permalink
syncx: SegmentKeysLock 支持分key加锁 (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
WeiJiadong authored Oct 13, 2023
1 parent afcc565 commit e2bf6f7
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# 开发中

- [syncx: 支持分key加锁](https://github.com/ecodeclub/ekit/pull/224)
# v0.0.8
- [atomicx: 泛型封装 atomic.Value](https://github.com/gotomicro/ekit/pull/101)
- [queue: API 定义](https://github.com/gotomicro/ekit/pull/109)
Expand Down
71 changes: 71 additions & 0 deletions syncx/segment_key_lock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2021 ecodeclub
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package syncx

import (
"hash/fnv"
"sync"
)

// SegmentKeysLock 部分key lock结构定义
type SegmentKeysLock struct {
locks []*sync.RWMutex
}

// NewSegmentKeysLock 创建 SegmentKeysLock 示例
func NewSegmentKeysLock(size int) *SegmentKeysLock {
locks := make([]*sync.RWMutex, size)
for i := range locks {
locks[i] = &sync.RWMutex{}
}
return &SegmentKeysLock{
locks: locks,
}
}

// hash 索引锁的hash函数
func (s *SegmentKeysLock) hash(key string) uint32 {
h := fnv.New32a()
h.Write([]byte(key))
return h.Sum32()
}

// RLock 读锁加锁
func (s *SegmentKeysLock) RLock(key string) {
hash := s.hash(key)
lock := s.locks[hash%uint32(len(s.locks))]
lock.RLock()
}

// RUnlock 读锁解锁
func (s *SegmentKeysLock) RUnlock(key string) {
hash := s.hash(key)
lock := s.locks[hash%uint32(len(s.locks))]
lock.RUnlock()
}

// Lock 写锁加锁
func (s *SegmentKeysLock) Lock(key string) {
hash := s.hash(key)
lock := s.locks[hash%uint32(len(s.locks))]
lock.Lock()
}

// Unlock 写锁解锁
func (s *SegmentKeysLock) Unlock(key string) {
hash := s.hash(key)
lock := s.locks[hash%uint32(len(s.locks))]
lock.Unlock()
}
74 changes: 74 additions & 0 deletions syncx/segment_key_lock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2021 ecodeclub
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package syncx

import (
"sync"
"sync/atomic"
"testing"

"github.com/stretchr/testify/assert"
)

func TestSegmentKeysLock(t *testing.T) {
s := NewSegmentKeysLock(10)
key := "test_key"
var wg sync.WaitGroup
wg.Add(2)
var writeDone atomic.Bool
var readStarted atomic.Bool
val := false
cond := sync.NewCond(&sync.Mutex{})
cond.L.Lock()

// 写 goroutine
go func() {
defer wg.Done()
s.Lock(key)
val = true // 加写锁写
s.Unlock(key)
writeDone.Store(true)
cond.Broadcast()
}()

// 读 goroutine
go func() {
defer wg.Done()
cond.L.Lock()
defer cond.L.Unlock()

// 等待写操作完成
for !writeDone.Load() {
cond.Wait()
}

readStarted.Store(true)
cond.Broadcast()
s.RLock(key)
assert.Equal(t, true, val, "Read lock err") // 加读锁读
defer s.RUnlock(key)
}()

// 等待读操作开始
for !readStarted.Load() {
cond.Wait()
}

// 检查写操作是否已完成,防止意外情况导致读优先写发生
assert.Equal(t, true, writeDone.Load(), "Write operation did not complete before read operation started")

cond.L.Unlock()
wg.Wait()
}

0 comments on commit e2bf6f7

Please sign in to comment.