【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
}
相关推荐
weixin_472339462 小时前
高效处理大体积Excel文件的Java技术方案解析
java·开发语言·excel
枯萎穿心攻击3 小时前
响应式编程入门教程第二节:构建 ObservableProperty<T> — 封装 ReactiveProperty 的高级用法
开发语言·unity·c#·游戏引擎
Eiceblue4 小时前
【免费.NET方案】CSV到PDF与DataTable的快速转换
开发语言·pdf·c#·.net
tan180°5 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
m0_555762905 小时前
Matlab 频谱分析 (Spectral Analysis)
开发语言·matlab
浪裡遊6 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
优创学社26 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
why技术6 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理6 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
lzb_kkk7 小时前
【C++】C++四种类型转换操作符详解
开发语言·c++·windows·1024程序员节