前言:
目前小伙伴们对“禁止复制粘贴代码”大体比较看重,小伙伴们都需要分析一些“禁止复制粘贴代码”的相关资讯。那么小编在网上搜集了一些对于“禁止复制粘贴代码””的相关文章,希望小伙伴们能喜欢,你们一起来了解一下吧!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版本中 Mutex和RWMutex中,官方都实现了 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 }
标签: #禁止复制粘贴代码