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

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

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

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

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!