Bootstrap

【Golang】redis分布式锁与本地互斥锁自由切换 (1) 基于本地缓存的互斥锁

互斥锁管理器 - 基于本地缓存的互斥锁

我们想实现redis的分布式锁和本地互斥锁自由切换。本文先实现一种基于本地缓存实现的互斥锁管理器,介绍其设计和实现细节。

代码结构

代码主要包括以下几个部分:

  1. mumgrLocal 结构体和初始化函数
  2. mutexLocal 结构体及其方法
  3. 基于缓存的互斥锁管理逻辑
mumgrLocal 结构体
type mumgrLocal struct {
	sync.RWMutex
	caches *cache.Cache
	retry  int
	times  int
}

mumgrLocal 结构体包含以下字段:

  • RWMutex:读写锁,用于保护对内部数据的并发访问。
  • caches:一个指向本地缓存对象的指针,用于存储互斥锁。此处cache每get一次会更新过期时间。
  • retrytimes:控制锁重试和等待时间的参数。
初始化函数
var _mumgrLocal *mumgrLocal

func initMutexLocal(retry int, times int, expire int, cleanup int) *mumgrLocal {
	if retry <= 0 {
		retry = 100
	}
	if times <= 0 {
		times = 30
	}
	_mumgrLocal = &mumgrLocal{
		retry: retry, 
		times: times, 
		caches: cache.New(
			time.Duration(expire*int(time.Second)), 
			time.Duration(cleanup*int(time.Second))
		),
	}
	return _mumgrLocal
}

initMutexLocal 函数用于初始化 mumgrLocal 实例。它接受四个参数:重试次数(retry)、等待时间(times)、缓存过期时间(expire)和缓存清理时间(cleanup)。如果重试次数或等待时间不合理,函数会设置默认值。

创建新锁
func (m *mumgrLocal) NewMutex(name string, opts ...redsync.Option) MutexInterface {
	m.Lock()
	defer m.Unlock()
	if value, ok := m.caches.Get(name); ok {
		return value.(*mutexLocal)
	}
	lock := &mutexLocal{name: name}
	m.caches.Set(name, lock, cache.DefaultExpiration)
	return lock
}

NewMutex 方法用于创建一个新的互斥锁。它首先尝试从缓存中获取锁,如果存在则直接返回;否则创建一个新的 mutexLocal 实例并存入缓存。

mutexLocal 结构体
type mutexLocal struct {
	name   string
	locked int32
}

mutexLocal 结构体表示一个具体的互斥锁。它包含两个字段:锁的名称和一个表示锁状态的整型变量。

锁的实现
func (m *mutexLocal) Lock() error {
	bdlog.Debug("Lock start, %p", m)
	for i := 0; i < int(_mumgrLocal.retry); i++ {
		if atomic.CompareAndSwapInt32(&m.locked, 0, 1) {
			return nil
		} else {
			time.Sleep(time.Duration(_mumgrLocal.times) * time.Millisecond)
		}
	}
	return fmt.Errorf("lock time out")
}

func (m *mutexLocal) Unlock() (bool, error) {
	bdlog.Debug("Unlock start, %p", m)
	if atomic.CompareAndSwapInt32(&m.locked, 1, 0) {
		bdlog.Debug("Unlock success")
	}
	return true, nil
}

Lock 方法尝试获取锁,使用 CAS(Compare-And-Swap)操作保证原子性。如果锁定成功则返回 nil,否则在设定的重试次数内等待并重试,最终返回超时错误。

Unlock 方法释放锁,同样使用 CAS 操作确保原子性。如果解锁成功,则记录日志并返回。

代码分析
  1. 线程安全:使用读写锁和 CAS 操作保证了锁的创建和访问是线程安全的。
  2. 本地缓存:基于本地缓存实现锁的存储和管理,减少了网络通信开销,适用于同一进程内的多线程场景。
  3. 重试机制:通过设置重试次数和等待时间,提高了锁获取的成功率,避免了资源竞争导致的长时间等待。
总结

本文介绍了一种基于本地缓存实现的互斥锁管理器的设计与实现。通过合理使用 Go 语言的并发原语和缓存机制,可以在多线程环境中高效地管理锁,提高系统的性能和可靠性。希望这篇文章能为读者在开发并发程序时提供一些有益的参考。

;