在用go实现raft之时,有众多的变量需要加锁,遇到了很多问题,以下为这些思考的总结
考虑极端的两种情况
稍微总结一下可以看出,我们希望能尽可能避免死锁,尽可能减小锁的粒度来提升性能,同时也要保证有便捷的方式来同时锁住多个变量。想要实现这些目的,就不可能省事(o_o …
以下提出我所思考到的几种解决方案,相互不一定冲突,亦只作为抛砖引玉,渴望大佬们赏光与赐教。
对于一个有十几个变量的对象,将相对独立的功能,或者几个内聚性比较强的变量,拆成一个独立的子对象。子对象对原本的对象提供方法,不提供变量。
这个直接举例子吧
type Obj struct { mu sync.Mutex } func (obj *Obj) FuncA (isLock ...bool){ if len(isLock) == 0 || isLock[0] { obj.mu.Lock() defer obj.mu.Unlock() } ... } func (obj *Obj) FuncB (isLock ...bool){ if len(isLock) == 0 || isLock[0] { obj.mu.Lock() defer obj.mu.Unlock() } ... } func (obj *Obj) FunC (isLock ...bool){ if len(isLock) == 0 || isLock[0] { rf.raftTermState.mu.Lock(); defer rf.raftTermState.mu.Unlock() } obj.FuncA(false) obj.FuncB(false) ... }最经典最常见的版本控制,应该是在关系型数据库中使用到的MVCC(我没怎么了解过其实现),但是基于这一思想,想到了一些别的使用方式。
当我需要发一个http或者RPC请求时,如果等网络IO完才释放锁,那么性能一定很差。
加锁 -> 将要发送的数据取出 -> 释放锁 -> 发送网络请求 -> 收到回复之后再加锁 -> 修改状态 -> 释放锁。
这一过程也可以视为将自身的数据存在了请求(Request)之中(将自身的数据拍快照,存在Request之中),简化了实现的思路,性能上也还行。