【go每日一题】 channel实现mutex锁

代码实现

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
}
相关推荐
EutoCool6 分钟前
Qt:布局管理器Layout
开发语言·c++·windows·嵌入式硬件·qt·前端框架
Cyanto40 分钟前
Spring注解IoC与JUnit整合实战
java·开发语言·spring·mybatis
写不出来就跑路1 小时前
WebClient与HTTPInterface远程调用对比
java·开发语言·后端·spring·springboot
悠哉清闲1 小时前
C++ MediaCodec H264解码
开发语言·c++
张人玉1 小时前
c#中Random类、DateTime类、String类
开发语言·c#
Jinkxs2 小时前
JavaScript性能优化实战技术
开发语言·javascript·性能优化
天上掉下来个程小白2 小时前
MybatisPlus-06.核心功能-自定义SQL
java·spring boot·后端·sql·微服务·mybatisplus
知了一笑2 小时前
独立开发第二周:构建、执行、规划
java·前端·后端
寻月隐君2 小时前
想用 Rust 开发游戏?这份超详细的入门教程请收好!
后端·rust·github
晴空月明2 小时前
分布式系统高可用性设计 - 缓存策略与数据同步机制
后端