通过chan实现互斥锁
利用 select+chan 的方式,很容易实现 TryLock、Timeout 的功能
原理
chan中有一把锁lock 可以保护chan中的字段,同时chan的send和recev 两种角色存在一种hapends-befores的关系。
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
第一种方法
第一种方法指的是chan的cap为1,放入一个元素代表获得锁,谁获得了这个元素则获取到锁。
type Mutex struct {
ch chan struct{}
}
// init clock
func NewMutex() *Mutex {
mutex := &Mutex{
ch: make(chan struct{}, 1),
}
mutex.ch <- struct{}{}
return mutex
}
// get lock
func (m *Mutex) Lock() {
<-m.ch
}
// return lock
func (m *Mutex) Unlock() {
select {
case m.ch <- struct{}{}:
default:
panic("unlock the unlocked mutex")
}
}
// try get lock
func (m *Mutex) TryLock() bool {
select {
case <-m.ch:
return true
default:
return false
}
}
func (m *Mutex) LockTimeout(timeout time.Duration) bool {
timer := time.NewTimer(timeout)
select {
case <-timer.C:
case <-m.ch:
timer.Stop()
return true
}
return false
}
func (m Mutex) IsLocked() bool {
return len(m.ch) == 0
}
第二种方法
第二种法法指的是chan的cap为1,空槽为锁,能成功发送到chan的则获取到了锁。
type Mutex struct {
ch chan struct{}
}
// init clock
func NewMutex() *Mutex {
mutex := &Mutex{
ch: make(chan struct{}, 1),
}
return mutex
}
// get lock
func (m *Mutex) Lock() {
m.ch <- struct{}{}
}
// return lock
func (m *Mutex) Unlock() {
select {
case <-m.ch :
default:
panic("unlock the unlocked mutex")
}
}
// try get lock
func (m *Mutex) TryLock() bool {
select {
case m.ch <- struct{}{}:
return true
default:
return false
}
}
func (m *Mutex) LockTimeout(timeout time.Duration) bool {
timer := time.NewTimer(timeout)
select {
case <-timer.C:
case m.ch <- struct{}{}:
timer.Stop()
return true
}
return false
}
func (m Mutex) IsLocked() bool {
return len(m.ch) == 1
}
引用:go 并发编程实战课 — 鸟窝