【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
}
相关推荐
HyperAI超神经21 分钟前
【vLLM 学习】Aqlm 示例
java·开发语言·数据库·人工智能·学习·教程·vllm
小柒的博客32 分钟前
从C语言变量看内存
c语言·开发语言
常年游走在bug的边缘34 分钟前
基于spring boot 集成 deepseek 流式输出 的vue3使用指南
java·spring boot·后端·ai
炯哈哈1 小时前
【上位机——MFC】菜单类与工具栏
开发语言·c++·mfc·上位机
廖广杰1 小时前
java虚拟机-为何元空间取代永久代
后端
李菠菜1 小时前
配置 MySQL 8 允许 Root 用户远程访问
后端·mysql
稀土君1 小时前
🔥 万「友」引力计划上线啦,轻松做任务赢积分“拿”华为MatePad Air、雷蛇机械键盘、 热门APP会员卡...
前端·人工智能·后端
2401_846535951 小时前
spark和hadoop的区别与联系
开发语言·spark
tactfulleaner1 小时前
小于n的最大数
后端
Bohemian1 小时前
服务稳定性建设之隔离机制 学习笔记
后端·微服务·面试