go 如何实现 try-lock?
2022-11-13 本文已影响0人
wayyyy
在某些场景,我们只希望一个任务有单一的执行者,而不像计数器一样,所有的 Goroutine 都成功执行。后续的 Goroutine 在抢锁失败后,需要放弃执行,这时候就需要尝试加锁trylock。
// Lock try lock
type Lock struct {
c chan struct{}
}
// Lock try lock, return lock result
func (l Lock) Lock() bool {
result := false
select {
case <-l.c:
result = true
default:
}
return result
}
// Unlock the try lock
func (l Lock) Unlock() {
l.c <- struct{}{}
}
// NewLock generate a try lock
func NewLock() Lock {
var l Lock
l.c = make(chan struct{}, 1)
l.c <- struct{}{}
return l
}
func main() {
var lock = NewLock()
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if !lock.Lock() {
println("lock failed")
return
}
counter++
println("current counter: ", counter)
lock.Unlock()
}()
}
wg.Wait()
}
每个 Goruntine 只有成功执行了 Lock()
才会继续执行后续代码,因此在Unlock()
时可以保证 Lock 结构体里的 Channel 一定是空的,所以不会阻塞也不会失败。
但要注意 trylock 有时并不是一个好选择,因为大量的 Goruntine 抢锁会无意义地占用 cpu 资源,这就是活锁。活锁指是的程序看起来在正常执行,但 cpu 周期被浪费在抢锁而非执行任务上,从而程序整体的执行效率低下。