在EasyNVR视频平台使用的视频直播项目场景中,经常会有多路推拉流的场景出现,因为基本是采用异步调用的方式,所以在多并发的情况下会出现数据不安全问题,这个时候就需要使用锁,来进行协程数据安全的处理。
Go语言包中的 sync 包提供了两种锁类型:sync.Mutex 和 sync.RWMutex。Mutex为互斥锁,适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景;RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景。
sync.Mutex 锁
该锁的结构体定义如下:
代码语言:javascript复制// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {
state int32
sema uint32
}
该结构体主要实现了 Locker 接口,对外部暴露两个方法Lock() 和 Unlock()
代码语言:javascript复制// A Locker represents an object that can be locked and unlocked.
type Locker interface {
Lock()
Unlock()
}
该种锁的特点是,一旦 Lock() 锁定,其他所有协程都不能访问对应的数据,除非该协程调用 Unlock() 将锁释放出来。
在实际编码使用中,很少使用 sync.Mutex 锁,该种锁使用其他比较粗暴,为考虑读写的场景,在很多场景中,经常会有该种需求,一份数据在被读取的时候,其他协程也可以读取,但是一份数据在被写入新的数据时,不允许其他协程读写,因此又出现了 sync. RWMutex 这种读写锁的出现。读写锁,在实际使用中更高效。
sync. RWMutex锁
代码语言:javascript复制type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}
读写锁也是依靠 sync.Mutex 这种锁来进行,该锁主要提供 RLock()、URLock()、Lock()和Unlock() 四个方法来。读锁为 RLock()方法,写锁为 Lock() 方法。其中对于读锁,不允许递归调用。以下是读锁的使用方式:
代码语言:javascript复制func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
sync 包中还提供了一些 sync.Map 和 sync.WaitGroup 等试用的结构体,在遇见对应的场景下再进行介绍。