kmutex源码分析

昨天偶然阅读pouch项目的源码发现有个kmutex库看了下代码发现挺有意思的。根据chan的特性,进行上锁、解锁,某些场景下很是方便!

源码详情地址:github


首先是结构体有两个

type KMutex struct {
	sync.Mutex
	keys map[string]*value
}

type value struct {
	c     chan struct{}
	waits int32
}

KMutex里面有一个互斥锁跟一个map类型,value里面有一个chan 跟一个int32类型
他们之间的关系,需要我用代码解读才能带你领略,流程都是正常的从上到下
首先是New

func New() *KMutex {
	m := &KMutex{
		keys: make(map[string]*value),
	}

	ticker := time.NewTicker(time.Minute)
	go func() {
		for {
			<-ticker.C

			m.Mutex.Lock()

			for k, v := range m.keys {
				if v.waits == 0 {
					delete(m.keys, k)
					close(v.c)
				}
			}

			m.Mutex.Unlock()
		}
	}()

	return m
}

首先是初始化structmap,接下来他定义了一个定时器然后一分钟执行一次会把KMutex中的map key跟v全部清除.由于这不是一个通用库所以他们这个定时器我也不太确定是什么意思,gc or 部分需求。

func (m *KMutex) Lock(k string) bool {
	m.Mutex.Lock()

	v, ok := m.lock(k)
	if ok {
		m.Mutex.Unlock()
		return true
	}

	atomic.AddInt32(&v.waits, 1)
	defer atomic.AddInt32(&v.waits, -1)

	m.Mutex.Unlock()

	<-v.c
	return true
}

重点的上锁代码首先互斥锁上锁,然后调用自身的一个内部方法lock,让我看下这个函数的是啥原理吧

func (m *KMutex) lock(k string) (*value, bool) {
	v, ok := m.keys[k]
	if !ok {
		m.keys[k] = &value{
			c:     make(chan struct{}, 1),
			waits: 0,
		}
		return nil, true
	}

	return v, false
}

首先根据参数从map中获取数据,如果map中没有这个key,初始化这个key,并返回不存在,反之把获取到的value返回,并且存在!
lock函数的大致意思是根据key从map中判断是否存在,不存在返回[nil,true],存在返回[values,false].


接着上面调用自身的lock方法,如果返回true那么直接宣布上锁成功,因为如果lock返回ture,代表这个key没有被使用过。

接下来使用atomic的原子性来记录有多少人再等待获取这个锁。
首先+1,然后defer -1看清楚是defer代表着这行代码要到函数return后才会执行
然后解锁,然后再使用chan的特性堵塞住代码!

func (m *KMutex) Unlock(k string) {
	m.Mutex.Lock()
	defer m.Mutex.Unlock()

	v, ok := m.keys[k]
	if ok && len(v.c) == 0 {
		v.c <- struct{}{}
	}
}

然后说下Unlock解锁代码,这里就比较简单,互斥锁上锁,根据key获取vale然后判断成功与否,跟chan里面的数量,然后往chan里面push值,这样会解除chan带来的堵塞!


剩下的LockWithTimeoutTrylock我就说下Trylock吧,LockWithTimeout代码跟Lock没啥差别就是结尾做了个select判断。

// Trylock trys to lock, will not block.
func (m *KMutex) Trylock(k string) bool {
	m.Mutex.Lock()
	defer m.Mutex.Unlock()

	v, ok := m.lock(k)
	if ok {
		return true
	}

	select {
	case <-v.c:
		return true
	default:
		return false
	}
}

这段代码再select都跟Lock差不多,如果go基础足够深的话就能看出为啥。
如果select加上default的话那么不会构成死锁,并且不会堵塞!

结尾说两句

上面的见解仅限于我个人,可能表达的不是很清楚,请谅解!比较语文作文没及格过。源码需要多看多想!更重要的是多动手!

你可能感兴趣的:(Golang)