龙空技术网

go语言并发原语Mutex易错场景及自定义锁

石老师小跟班 128

前言:

目前小伙伴们对“禁止复制粘贴代码”大体比较看重,小伙伴们都需要分析一些“禁止复制粘贴代码”的相关资讯。那么小编在网上搜集了一些对于“禁止复制粘贴代码””的相关文章,希望小伙伴们能喜欢,你们一起来了解一下吧!

1. Mutex易错场景1.1 Lock/Unlock不是成对出现

尽量使用defer 语句处理

1.2 copy 已使用的mutex

知识点:同步原语在使用后是不可复制的,像函数的参数,函数的返回值,都需要关注

可以使用 go vet 工具 是否有锁被复制的情况call of copy copies lock value: sync.Mutex

1.3 重入

Mutex是不可重入锁,如果出现重入,会发生死锁,如下

 func main() {     l := &sync.Mutex{}     foo(l) } func foo(l sync.Locker) {     fmt.Println("in foo")     l.Lock()     bar(l)     l.Unlock() }  func bar(l sync.Locker) {     l.Lock()     fmt.Println("in bar")     l.Unlock()  }

自己实现可重入锁

1.4 死锁

发生死锁的条件:

互斥:至少一个资源是被排他性独享的,其他线程必须处于等待状态,直到资源被释放持有和等待:goroutine持有一个资源,并且还在请求其他goroutine持有的资源不可剥夺:资源只能由它的goroutine来释放环路等待:资源互相等待,形成一个环路等待的死结

如里避免死锁,只要使上述4个条件中一个条件不成立,既可避免出现死锁

2.基于Mutex实现可重入锁2.1 基于 goroutine id来实现基于自己解析字符串实现获取goroutine id

//获取goroutine idfunc getId() int {	var buf [64]byte	n := runtime.Stack(buf[:], false)	idStr := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]	id, err := strconv.Atoi(idStr)	if err != nil {		panic(err)	}	return id}
基于第三方包petermattis/goid获取goroutine id,使用第三方时,需要关注支持的go版本
import ""github.com/petermattis/goid""//获取 goroutine idgoid.Get()
使用第三方包,实现可重入锁
type RecursiveMutex struct {	sync.Mutex	owner   int64 //当前持有锁的goroutine	recurse int32 //记录重入次数}func (r *RecursiveMutex) Lock() {	gid := goid.Get()	//说明是重入,	if atomic.LoadInt64(&r.owner) == gid {		r.recurse++		return	}	//不是重入,获取到锁,并写入重入次数为1	r.Mutex.Lock()	atomic.AddInt64(&r.owner, gid)	r.recurse = 1	return}func (r *RecursiveMutex) Unlock() {	gid := goid.Get()	if atomic.LoadInt64(&r.owner) != gid {		panic("unlock error")	}	r.recurse--	//还有锁直接返回	if r.recurse != 0 {		return	}	//如里没有了,需要释放锁	atomic.AddInt64(&r.owner, -1)	r.Mutex.Unlock()}
2.2 基于token方式来实现

实现方式和 基于 goroutine id实现的方式类似,只不过token是要从参数传入的,把判断 goroutine id的换成判断token即可,代码略

3基于Mutex实现 TryLock锁(获取不到锁,直接返回false,不阻塞)

在go1.18版本中 MutexRWMutex中,官方都实现了 TryLock方法

 /* * @author biubiu @date:2023/8/14 */ package lib  import (     "sync"     "sync/atomic"     "unsafe" )  const (     mutexLocked      = 1 << iota //加锁标志     mutexWoken                   //唤醒标志     mutexStarving                //锁饥饿标识位置     mutexWaiterShift = iota      //标识waiter的起始bit位置 )  type Mutex struct {     sync.Mutex }  func (m *Mutex) TryLock() bool {     //如果能成功获取到锁     if atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), 0, mutexLocked) {         return true     }     //如里处于唤醒,加锁,或者饥饿状态,这次请求就不参与竞争了,返回false     old := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))     if old&(mutexLocked|mutexWoken|mutexStarving) != 0 {         return false     }     //尝试在竞争状态下,请求锁     new := old | mutexLocked      return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), old, new) } 
4.基于Mutex数据结构,获取等待者数量等指标
 /* * @author biubiu @date:2023/8/14 */ package lib  import (     "sync"     "sync/atomic"     "unsafe" )  const (     mutexLocked      = 1 << iota //加锁标志     mutexWoken                   //唤醒标志     mutexStarving                //锁饥饿标识位置     mutexWaiterShift = iota      //标识waiter的起始bit位置 )  type Mutex struct {     sync.Mutex }  func (m *Mutex) TryLock() bool {     //如果能成功获取到锁     if atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), 0, mutexLocked) {         return true     }     //如里处于唤醒,加锁,或者饥饿状态,这次请求就不参与竞争了,返回false     old := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))     if old&(mutexLocked|mutexWoken|mutexStarving) != 0 {         return false     }     //尝试在竞争状态下,请求锁     new := old | mutexLocked      return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), old, new) } func (m *Mutex) Count() int {     v := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))     v = v >> mutexWaiterShift //得到等待者的数量     v = v + (v & mutexLocked) //再加上锁持有值的数量, 0或1     return int(v) } func (m *Mutex) IsLocked() bool {     state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))     return state&mutexLocked == mutexLocked } func (m *Mutex) IsWoken() bool {     state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))     return state&mutexWoken == mutexWoken } func (m *Mutex) IsStarving() bool {     state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex)))     return state&mutexStarving == mutexStarving } 
5.基于Mutex实现线程安全的队列
 /* * @author biubiu @date:2023/8/14 */ package lib  import "sync"  type SliceQueue struct {     data []interface{}     mu   sync.Mutex }  func NewSliceQueue(n int) (q *SliceQueue) {     return &SliceQueue{         data: make([]interface{}, 0, n),     } }  /** * @Author: biubiu * @Description: 加入队列 * @receiver s * @param v  */ func (s *SliceQueue) Enqueue(v interface{}) {     s.mu.Lock()     s.data = append(s.data, v)     s.mu.Unlock() } func (s *SliceQueue) Dequeue() interface{} {     s.mu.Lock()     if len(s.data) == 0 {         s.mu.Unlock()         return nil     }     v := s.data[0]     s.data = s.data[1:]     s.mu.Unlock()     return v } 

标签: #禁止复制粘贴代码