var objectKey = "db:cms:pblish:"+ gconv.String(entity.ContentId) locker := redislock.NewRedisLocker(objectKey, gconv.String(time.Now().UnixNano()), 120*time.Second) if entity.ContentId > 0 { var lock, err = locker.Lock() if err != nil || lock == false { golog.Error("正在操作,请稍后再试!") return nil, errors.New("正在操作,请稍后再试!") } defer locker.Unlock() 此处的unlock会在if内释放吗?
在您提供的代码中,defer locker.Unlock()
的释放时机与 if
代码块的关系如下:
关键点分析:
-
defer
的执行机制:
Go 的defer
语句不会在if
块结束时立即执行,而是延迟到当前函数返回时才执行(无论函数是正常返回还是因错误/panic 退出)。 -
代码中的具体行为:
go
复制
下载
if entity.ContentId > 0 { // ... 获取锁成功 defer locker.Unlock() // 注册延迟调用 } // 这里 if 块结束,但 Unlock 不会在此处执行!
-
defer locker.Unlock()
是在if
块内部注册的。 -
当程序执行到
if
块末尾时(}
),锁不会释放。 -
锁的释放实际发生在整个外层函数返回时(即执行
return
语句或函数自然结束时)。
-
可能的问题:
如果 if
块之后的代码逻辑耗时较长:
go
复制
下载
if entity.ContentId > 0 { // 获取锁... defer locker.Unlock() // 注册释放 } // 这里是后续代码(可能耗时很久) time.Sleep(30 * time.Second) // 锁会持续持有 30 秒!
-
锁会持续占用直到整个函数返回,可能导致其他请求无法获取锁。
解决方案建议:
若需要提前释放锁(不等待函数返回),应手动调用 Unlock
:
go
复制
下载
if entity.ContentId > 0 { lock, err := locker.Lock() if err != nil || !lock { // ... 处理错误 } defer locker.Unlock() // 确保函数退出时释放(兜底) // 执行需要加锁的操作... doCriticalWork() // 主动提前释放锁(不再需要时) locker.Unlock() // 注意:defer 仍会尝试释放,需确保 Unlock 可重复调用或无副作用 }
总结:
-
不会在
if
内释放:defer locker.Unlock()
在if
块结束时不会触发,而是在外层函数返回时执行。 -
潜在风险:若加锁后还有耗时逻辑,会导致锁占用时间过长。
-
改进:在锁不再需要时手动调用
Unlock()
提前释放,并用defer
作为兜底(确保函数退出时释放)。