Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

syncx: SegmentKeysLock #224

Closed
flycash opened this issue Oct 6, 2023 · 3 comments · Fixed by #225
Closed

syncx: SegmentKeysLock #224

flycash opened this issue Oct 6, 2023 · 3 comments · Fixed by #225

Comments

@flycash
Copy link
Contributor

flycash commented Oct 6, 2023

仅限中文

使用场景

在很多时候,我们需要对 key 进行加锁。但是如果一个 key 一个锁,又很难管理,大概率是需要借助 sync.Map 来实现。

但是我们有一种更加简单的办法来达成类似的效果。

也就是将 key 进行哈希,获得对应的 lock 之后加锁。

行业分析

如果你知道有框架提供了类似功能,可以在这里描述,并且给出文档或者例子

可行方案

如果你有设计思路或者解决方案,请在这里提供。你可以提供多个方案,并且给出自己的选择

type SegmentKeysLock struct{
   // 理论上来说,不需要使用指针
    locks []sync.RWMutex
}

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

可以在初始化的时候直接初始化好全部的 locks,这样后续不管是加锁还是解锁,直接操作下标,都是只读的,所以不会有并发问题。

其它

任何你觉得有利于解决问题的补充说明

你使用的是 ekit 哪个版本?

你设置的的 Go 环境?

上传 go env 的结果

@WeiJiadong
Copy link
Contributor

是需要类似这样的实现吗?其实就是预申请很多把锁,对加解锁进行封装,以及通过hash的方式来取用锁。

package main

import (
	"fmt"
	"hash/fnv"
	"sync"
	"time"
)

type SegmentKeysLock struct {
	locks []sync.RWMutex
}

func NewSegmentKeysLock(size int) *SegmentKeysLock {
	return &SegmentKeysLock{
		locks: make([]sync.RWMutex, size),
	}
}

func (s *SegmentKeysLock) hash(key string) uint32 {
	h := fnv.New32a()
	h.Write([]byte(key))
	return h.Sum32()
}

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

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

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

func (s *SegmentKeysLock) Unlock(key string) {
	hash := s.hash(key)
	lock := &s.locks[hash%uint32(len(s.locks))]
	lock.Unlock()
}

func main() {
	lock := NewSegmentKeysLock(10)

	go func() {
		lock.Lock("key1")
		fmt.Println("goroutine 1 locked key1")
		time.Sleep(2 * time.Second)
		lock.Unlock("key1")
		fmt.Println("goroutine 1 unlocked key1")
	}()

	go func() {
		time.Sleep(1 * time.Second)
		lock.RLock("key1")
		fmt.Println("goroutine 2 read-locked key1")
		lock.RUnlock("key1")
		fmt.Println("goroutine 2 read-unlocked key1")
	}()

	time.Sleep(3 * time.Second)
}

@flycash
Copy link
Contributor Author

flycash commented Oct 8, 2023

是的

@WeiJiadong
Copy link
Contributor

好,已提交pr:
#225

@longyue0521 longyue0521 linked a pull request Oct 9, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants