代码实现
go
package test
import (
"fmt"
"strconv"
"testing"
"time"
)
type mutexCh struct { //应该大写,给外部所有包用
ch chan int // 私有变量,否则外部操作
}
func NewMutexCh() *mutexCh {
return &mutexCh{ch: make(chan int, 1)}
}
func (mc *mutexCh) TryLock() {
mc.ch <- 1
}
func (mc *mutexCh) TryUnlock() {
select {
case <-mc.ch:
default:
panic("unlock an unlocked lock")
}
}
func TestChLock(t *testing.T) {
mc := NewMutexCh()
m := map[int]string{
0: "hello",
1: "world",
}
for i := 0; i < 10; i++ {
go func(mc *mutexCh, m map[int]string, num int) {
mc.TryLock() //阻塞获取锁
m[0] = m[0] + strconv.Itoa(i)
mc.TryUnlock()
}(mc, m, i)
}
select {
default:
<-time.After(time.Second)
fmt.Println(m)
}
}
改进点
实际上应该提供Lock方法(阻塞等待),TryLock(如果被占有就返回False)
参考golang实现mutex的源码(内部实际上通过信号量实现)
go
// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// Slow path (outlined so that the fast path can be inlined)
m.lockSlow()
}
// TryLock tries to lock m and reports whether it succeeded.
//
// Note that while correct uses of TryLock do exist, they are rare,
// and use of TryLock is often a sign of a deeper problem
// in a particular use of mutexes.
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
}
使用select模拟tryLock()
go
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
}