Go: TryLock 函数介绍
Go版本1.18
Go1.18有一个新函数TryLock
(作为对互斥锁sunc.Mutex
与读写锁sync.RWMutex
的补充),它允许开发者在非阻塞模式下获取锁。如果锁已被获取,该函数将简单返回布尔值false
,而不是一直等待锁释放。
该函数激起我的好奇心,虽然通过其函数名就知道其意义,但该函数尚未有明确的示范用例。接下来让我们来解开它吧。
TryLock
工作流程
为了更好的理解互斥锁的工作流程,我建议你阅读这篇文章:Go: 互斥锁与饥饿
当出现以下情况时,互斥锁不可用:
- 该锁由任意
goroutine
持有时 - 锁没有被使用,但锁处于饥饿状态时,即该锁即将被下一个使用者持有
在以上两种情况下,尝试获取锁都将立即返回false
,以下是该函数对以上两种情况的说明:
该操作执行速度很快,只需要一位操作。
如果锁可用,协程将通过正常方式获取锁并返回该操作结果。如果锁不可用,协程将不会自旋或者以阻塞形式等待锁释放并获得锁。该操作是非阻塞的。
它解决什么问题呢?
官方文档说它的使用场景很少。
的确函数
TryLock
使用场景确实很少见,它通常是在特定场景下用以解决更深层次锁的问题
的确,很多年了,该功能在实际实践时未被使用,相关论坛也未有该函数的相关使用示例。那么它能给go社区带来什么呢?事实上,很多golang
包都尝试使用函数TryLock
,但它们在相关包中都未被有效融合。无论如何,有了官方的支持,在获取锁的竞争中它将变得更加易于使用。
Go标准库与其他语言
Go的源代码中并未使用该新函数。不过,在Go1.6版本runtime
有过相似的功能。它用于在运行时获取锁来实现堆栈扫描。如果运行时无法获取锁,则直接跳过扫描追踪。
其他语言Java
, Objective-C
等实现相同功能的方法。幸运的是,一个老版的JRE有实现该功能并给我们提供使用示例:该锁保护着一个正在负责清除队列的工作。队列可以被任意线程清除,所以第一个获取锁的线程执行清除操作,而其他线程则继续执行他们的工作。
TryLock
在go中的实现:
const (
mutexLocked = 1 << iota // mutex is locked
mutexWoken
mutexStarving
)
type Mutex sturct {
state int32
}
func (m *Mutex) TryLock() bool {
old := m.state
if old&(mutexLocked|mutexStarving) != 0 {
return false
}
// There may be a goroutine waiting for the mutex, but we are
// running now and can try to grab the mutex before that
// goroutine wakes up.
if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) {
return false
}
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return true
}
如果你有使用过该函数或了解该函数的使用场景,欢迎讨论。
本作品采用《CC 协议》,转载必须注明作者和本文链接
可以用于秒杀场景呢,加锁的成功就是可以进行后续操作,没抢到锁,就不管了吧~
「Go 学堂」写了一些列的 gin 框架的底层原理知识。还有相关的使用 go 语言写的开源包系列。欢迎关注