上一篇已经实现了获取锁和解锁的功能,但是获取锁是非阻塞的,也就是说获取锁失败后就立马返回失败结果了。在一些场景下,我们需要的是保持某一任务只有一个请求在执行,其他的请求在后面排队。就像公司只有一个厕所,每次只能有一个人在里面,其他人在外面排队,只是这个排队是抢占式的,谁速度快,谁就先。
接下来我们实现一个阻塞式的获取锁,代码如下
// Block 阻塞获取锁
func (lk *lock) Block(expiration time.Duration) bool {
t := time.Now()
for {
cxt, cancel := context.WithTimeout(context.Background(), 3*time.Second)
ok, err := redis.Client().SetNX(cxt, lk.key, lk.requestId, lk.expiration).Result()
cancel()
if err != nil {
return false
}
if ok {
return true
}
time.Sleep(200 * time.Millisecond)
if time.Now().Sub(t) > expiration {
return false
}
}
}
原理比较简单,就是在非阻塞获取锁的基础上加上一个循环,反复的获取锁,在这个期间要是超过了等待时间就返回false。
再实现一个强行释放锁的方法,某些场景下可能会用上
// ForceRelease 强制释放锁,忽略请求id
func (lk *lock) ForceRelease() error {
cxt, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
_, err := lk.connect.Del(cxt, lk.key).Result()
return err
}
调用实例
非阻塞
l := lock.NewLock("test", 10*time.Second)
defer l.Release()
if l.Get() {
return response.Resp().String("拿锁成功")
}
return response.Resp().String("拿锁失败")
阻塞
l := lock.NewLock("test", 10*time.Second)
defer l.Release()
if l.Block(5 * time.Second) {
return response.Resp().String("拿锁成功")
}
return response.Resp().String("拿锁失败")