当前位置: 首页 > article >正文

【Golang 面试题】每日 3 题(三十二)

✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/UWz06
📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

94. Go 互斥锁的底层实现结构

互斥锁对应的是底层结构是 sync.Mutex 结构体,,位于 src/sync/mutex.go 中

type Mutex struct {
     state int32
     sema  uint32
 }

state 表示锁的状态,有锁定、被唤醒、饥饿模式等,并且是用 state 的二进制位来标识的,不同模式下会有不同的处理方式。

在这里插入图片描述

sema 表示信号量,mutex 阻塞队列的定位是通过这个变量来实现的,从而实现 goroutine 的阻塞和唤醒。

在这里插入图片描述

addr = &sema
func semroot(addr *uint32) *semaRoot {
   return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
}
root := semroot(addr)
root.queue(addr, s, lifo)
root.dequeue(addr)
var semtable [251]struct {
   root semaRoot
   ...
}
type semaRoot struct {
  lock  mutex
  treap *sudog // root of balanced tree of unique waiters.
  nwait uint32 // Number of waiters. Read w/o the lock.
}
type sudog struct {
    g *g
    next *sudog
    prev *sudog
    elem unsafe.Pointer // 指向sema变量
    waitlink *sudog // g.waiting list or semaRoot
    waittail *sudog // semaRoot
    ...
}

95. Go 互斥锁的操作

锁的实现一般会依赖于原子操作、信号量,通过 atomic 包中的一些原子操作来实现锁的锁定,通过信号量来实现线程的阻塞与唤醒。

加锁

通过原子操作 cas 加锁,如果加锁不成功,根据不同的场景选择自旋重试加锁或者阻塞等待被唤醒后加锁。

在这里插入图片描述

func (m *Mutex) Lock() {
    // Fast path: 幸运之路,一下就获取到了锁
    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
        return
    }
    // Slow path:缓慢之路,尝试自旋或阻塞获取锁
    m.lockSlow()
}

解锁

通过原子操作 add 解锁,如果仍有 goroutine 在等待,唤醒等待的 goroutine。

在这里插入图片描述

func (m *Mutex) Unlock() {
   // Fast path: 幸运之路,解锁
   new := atomic.AddInt32(&m.state, -mutexLocked)
   if new != 0 {
            // Slow path:如果有等待的goroutine,唤醒等待的goroutine
            m.unlockSlow()
   }}

96. Go 互斥锁的使用注意事项

  • 在 Lock() 之前使用 Unlock() 会导致 panic 异常。
  • 使用 Lock() 加锁后,再次 Lock() 会导致死锁(不支持重入),需 Unlock() 解锁后才能再加锁。
  • 锁定状态与 goroutine 没有关联,一个 goroutine 可以 Lock,另一个 goroutine 可以 Unlock。

http://www.kler.cn/a/505252.html

相关文章:

  • 1️⃣Java中的集合体系学习汇总(List/Map/Set 详解)
  • 【区间DP】力扣3040. 相同分数的最大操作数目 II
  • Springboot 注解缓存使用教程
  • 【Rust自学】12.6. 使用TDD(测试驱动开发)开发库功能
  • 【高阶数据结构】位图
  • RabbitMQ故障全解析:消费、消息及日常报错处理与集群修复
  • SQL面试题1:连续登陆问题
  • Jenkins与不同阶段测试的完美结合
  • Github 2025-01-15 C开源项目日报 Top10
  • 【Linux】【文件】读文件的IO操作
  • 海云安开发者安全智能助手D10荣膺 “ AI标杆产品 ” 称号,首席科学家齐大伟博士入选2024年度 “ 十大杰出青年 ”
  • HarmonyOS NEXT开发进阶(七):页面跳转
  • 【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论和实操考试题解析
  • 504 Gateway Timeout:网关超时解决方法
  • 线程池底部工作原理
  • Matplotlib 图表显示比例控制笔记
  • iOS - block
  • 换了城市ip属地会变吗?为什么换了城市IP属地不变
  • Dubbo泛化调用
  • springMVC实现文件上传
  • Java中的反射机制:动态操作类的秘密武器
  • PHP 字符串
  • doris:手动分区
  • ChatGPT正在朝着全面个人助手迈出重要一步,推出了一个名为“Tasks”的新功能
  • 防火墙配置的关键要素
  • 使用Redis防止重复发送RabbitMQ消息