golang基于Mutex实现可重入锁

本文介绍了如何在Golang中使用Mutex实现非标准的可重入锁,通过获取goroutineID并记录锁的重入次数,避免了原生Mutex在重入时的阻塞问题。作者给出了详细的代码实现和测试用例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

golang基于Mutex实现可重入锁

锁重入的定义

锁可重入也就是当前已经获取到锁的goroutine继续调用Lock方法获取锁,Go标准库中提供了sync.Mutex实现了排他锁,但并不是可重入的,如果在代码中重入锁,也就是Lock之后再次进行Lock获取锁,则会被阻塞到第二次Lock上,锁没有办法得到释放从而影响其它goroutine执行

// 例如
package main;

import "sync"

func ReentryExample() {
	var c int64
	var mu sync.Mutex
	mu.Lock() // 第一次加锁
	// TODO //
	mu.Lock() // 第二次加锁,阻塞
	c++;
	// TODO ...
}

重入锁的简单实现思路

  1. 拿到能够识别到当前协程的id,(通过堆栈信息获取到goroutine的id)
  2. 写一个结构体,实现Locker接口

首先获取到goroutine的id

func GoID() int64 {
	var buf [32]byte
	n := runtime.Stack(buf[:],false) // 获取堆栈的信息
	// string(buf[:n] 
	/**
	 goroutine 6 [running]:
	 main.XXX	
	*/
	// 拿到goroutine的id
	goIdStr := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine"))[0]
	goId, err := strconv.Atoi(fieldId)// 转换为int
	return  int64(goId)
}

然后开始编写可重入锁的结构体

// ReentrantMutex 可重入的互斥锁
type ReentrantMutex struct {
	sync.Mutex       // 互斥锁
	goId       int64 // 用于保存goroutine的id
	recursion  int64 // 锁重入的次数
}

// Lock 实现Locker接口,用于加锁
func (r *ReentrantMutex) Lock() {
	gid := GoID()
	if atomic.LoadInt64(&r.goId) == gid { // 看看是否已经加过锁了?
		atomic.AddInt64(&r.recursion, 1) // 如果之前加过锁,则重入的次数+1
		return
	}
	r.Mutex.Lock() // 使用互斥锁上锁
	atomic.StoreInt64(&r.goId, gid) // 使用原子操作保存goroutine的id
	atomic.StoreInt64(&r.recursion, 1) // 第一次加锁,因此重入的次数为一
}

// Unlock 实现了Locker的接口,用于解锁
func (r *ReentrantMutex) Unlock() {
	gid := GoID()
	if atomic.LoadInt64(&r.goId) != gid { // 看是否加过锁
		panic("未加锁") // 没有加过锁,不存在解锁,直接panic
	}
	recursion := atomic.AddInt64(&r.recursion, -1) // 重入次数-1
	if recursion != 0 { // 如果重入次数没有等于0(意味着还有锁没有释放)
		return
	}
	atomic.StoreInt64(&r.goId, -1) // 重入次数为0,则不存在锁没有释放,解锁
	r.Mutex.Unlock() // 互斥锁解锁
}

测试用例

package main;

func main() {
	var m ReentrantMutex
	m.Lock()
	m.Lock() // 不会阻塞
	fmt.Println("1") // 正常打印1
	m.Unlock()
	m.Unlock()// 解锁
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值