Go: TryLock 函数介绍

Go: TryLock函数事故

Go 版本 1.18

Go1.18 有一个新函数 TryLock(作为对互斥锁 sunc.Mutex 与读写锁 sync.RWMutex 的补充),它允许开发者在非阻塞模式下获取锁。如果锁已被获取,该函数将简单返回布尔值 false,而不是一直等待锁释放。

该函数激起我的好奇心,虽然通过其函数名就知道其意义,但该函数尚未有明确的示范用例。接下来让我们来解开它吧。

TryLock 工作流程#

为了更好的理解互斥锁的工作流程,我建议你阅读这篇文章:Go: 互斥锁与饥饿

当出现以下情况时,互斥锁不可用:

  • 该锁由任意 goroutine 持有时
  • 锁没有被使用,但锁处于饥饿状态时,即该锁即将被下一个使用者持有

在以上两种情况下,尝试获取锁都将立即返回 false,以下是该函数对以上两种情况的说明:

Go: TryLock函数事故

该操作执行速度很快,只需要一位操作。

如果锁可用,协程将通过正常方式获取锁并返回该操作结果。如果锁不可用,协程将不会自旋或者以阻塞形式等待锁释放并获得锁。该操作是非阻塞的。

它解决什么问题呢?#

官方文档说它的使用场景很少。

的确函数 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
}

如果你有使用过该函数或了解该函数的使用场景,欢迎讨论。

原文地址:Go: Story of TryLock Function
译文地址:Go: TryLock 函数介绍

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 3

可以用于秒杀场景呢,加锁的成功就是可以进行后续操作,没抢到锁,就不管了吧~

2年前 评论
Zlw_LENG (楼主) 2年前

「Go 学堂」写了一些列的 gin 框架的底层原理知识。还有相关的使用 go 语言写的开源包系列。欢迎关注

2年前 评论