Go面试看这里了~(五)-Mutex锁相关

原文地址:Go面试看这里了~(五)

1、mutex的正常模式和饥饿模式?

正常模式(非公平锁):所有等待锁的goroutine按先进先出的顺序等待,唤醒的goroutine不会直接拥有锁,而是回合新请求锁的goroutine竞争锁的拥有权,新请求锁的goroutine有优势(1、正在CPU上执行;2、可能同时拥有好几个锁),所以刚刚唤醒的goroutine很大可能在竞争锁时失败,此时,这个刚唤醒的goroutine会加入到等待队列的前面,如等待的goroutine等待时间超过1ms没获取到锁,此goroutine将会把锁转变为饥饿模式。

饥饿模式(公平锁):是为了解决等待goroutine队列过长问题,此模式下,直接由unlock将锁交给队列中排在第一位的goroutine,同时,新进来的goroutine不会参与抢锁,也不会进入自旋状态,会直接进入队列尾部,如此就解决了老goroutine一直抢不到锁的场景。

饥饿模式的触发条件:

  1. goroutine等待锁时间超过1ms。

  2. 当前队列只剩下一个goroutine。

总结来看的话,正常模式下的性能是最好的,goroutine可连续多次获取锁,饥饿模式解决了取锁公平的问题,但是性能会下降,是性能和公平性的一个平衡模式。

2、mutex允许自旋的条件?

  1. 锁已被占用,且不处于饥饿模式。

  2. 积累的自旋次数小于最大自旋次数(active_spin=4)。

  3. CPU核数大于1。

  4. 有空闲的p。

  5. 当前goroutine所挂载的p下的本地待运行队列为空。

3、RWMutex的实现?

通过积累readerCount读锁数量来进行控制,当有一个写锁时,会将读锁数量设置为-1<<30,目的是让新进入的读锁等待写锁释放之后通知读锁,写锁也会等待之前的读锁释放完毕之后,才会进行后面的操作,写锁释放之后,会将值重新加上1<<30,并通知刚刚新进入的读锁(rw.readerSem),两者互相限制。

4、RWMutex的注意事项?

  1. RWMutex是单写多读锁,该锁可加多个读锁或一个写锁。

  2. 读锁占用情况下会阻止写,不阻止读,多个goroutine可同时获取读锁。

  3. 写锁会阻止其它goroutine(无论读写)进来,整个锁由该goroutine独占。

  4. 适用于读多写少场景。

  5. RWMutex类型变量的零值为未锁定状态的互斥锁。

  6. RWMutex首次被使用后就不能再被拷贝。

  7. RWMutex读锁或写锁在未锁定状态时,解锁会引发panic。

  8. RWMutex一个写锁去锁定临界区的共享资源,如临界区的共享资源已被锁定,进行此操作的goroutine将会被阻塞到解锁。

  9. RWMutex读锁不要用于递归调用,易产生死锁。

  10. RWMutex锁定状态与特定goroutine无关,一goroutine可RLock(Lock),另一goroutine可RUnlock(Unlock)。

  11. 写锁被解锁后,所有因操作锁定读锁而被阻塞的goroutine会被唤醒,并都可成功锁定读锁。

  12. 读锁被解锁后,在未被其它读锁锁定前提下,所有因操作锁定写锁而被阻塞的goroutine,其中等待时间最长的一个goroutine会被唤醒。

5、Cond是什么?

Cond实现一种条件变量,可用在多个reader等待共享资源的ready的场景(如只有一读一写,一个锁或channel就搞定),每个Cond都会关联一个Lock(*sync.Mutex或*sync.RWMutex),当修改条件或调用wait时,必须加锁,保护condition。

6、Broadcast和Signal的区别?

func (c *Cond) Broadcast()func (c *Cond) Signal()

Broadcast会唤醒所有等待C的goroutine,调用Broadcast可加锁,也可不加。

Signal只唤醒一个等待C的goroutin,调用Signal时可加锁,也可不加。

7、Cond中Wait的使用?

func (c *Cond) Wait()c.L.Lock()for !condition() { c.Wait()}... make use of condition ...c.L.Unlock()

wait会自动释放c.L并挂起调用者的goroutine,之后恢复执行,wait会在返回时对c.L加锁,除非被Broadcast或Signal唤醒,否则wait不会返回。

由于wait第一次恢复时,c.L并未加锁,所以当wait返回时,调用者不能假定条件为真,取而代之的是,调用者应在循环中调用wait。

简单来讲,想使用condition,就得加锁。

至此,本次分享就结束了,后期会慢慢补充。

以上仅为个人观点,不一定准确,能帮到各位那是最好的。

好啦,到这里本文就结束了,喜欢的话就来个三连击吧。

扫码关注公众号,获取更多优质内容。

### Golang Mutex 面试题及相关答案 在并发编程领域,`sync.Mutex` 是 Go 中用于保护共享资源免受竞争条件影响的重要工具之一。以下是几个常见的关于 `Mutex` 的面试题及其解答: #### 1. **什么是互斥 (Mutex),以及它在 Go 中的作用是什么?** 互斥是一种同步原语,用于防止多个 Goroutine 同时访问共享数据结构[^2]。Go 提供了 `sync.Mutex` 类型来实现这一功能。通过调用其方法 `Lock()` 和 `Unlock()` 可以分别获取和释放。 ```go var mu sync.Mutex mu.Lock() // 获取 // 访问共享资源 mu.Unlock() // 释放 ``` #### 2. **为什么需要使用 Mutex 来解决竞态条件问题?** 当多个 Goroutine 并发运行并尝试修改同一个变量时,可能会发生不可预测的行为。这种现象称为竞态条件[^3]。为了确保程序的一致性和正确性,在任何时间点只允许一个 Goroutine 修改该变量,这就是 Mutex 发挥作用的地方。 #### 3. **下这段代码存在什么潜在问题?如何修复这个问题?** ```go package main import ( "fmt" ) func main() { var count int for i := 0; i < 1000; i++ { go func() { count++ }() } fmt.Println(count) } ``` 上述代码中的主要问题是未加的情况下对全局变量 `count` 进行操作可能导致竞态条件[^4]。可以通过引入 `sync.Mutex` 解决此问题: ```go package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup var mu sync.Mutex count := 0 for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() mu.Lock() count++ mu.Unlock() }() } wg.Wait() fmt.Println("Final Count:", count) } ``` 这里增加了 WaitGroup 确保主线程等待所有子线程完成执行后再打印最终计数值;同时利用 Mutex 定每次更新过程从而避免冲突。 #### 4. **RWMutexMutex 有什么区别?什么时候应该选择 RWMutex 而不是普通的 Mutex?** 标准库还提供了另一种类型的读写——`sync.RWMutex` ,它可以允许多个读者或者单个写者持有定状态[^5]。如果场景中有大量读取而很少写的操作,则可以考虑采用 RWMutex 提高性能。 - 使用普通 Mutex : 所有请求都需要排队等候独占权限。 - 使用 RWMutex :支持多路并发读取而不阻塞其他读取动作直到某个写入开始为止。 示例对比两者差异如下所示 : ```go type Counter struct { mutex *sync.Mutex rwmtx *sync.RWMutex value int } func NewCounter(useRW bool) *Counter { if useRW { return &Counter{rwmtx: new(sync.RWMutex)} } else { return &Counter{mutex: new(sync.Mutex)} } } func (c *Counter) Inc() { if c.mutex != nil{ c.mutex.Lock(); defer c.mutex.Unlock(); }else{ c.rwmtx.Lock();defer c.rwmtx.Unlock(); } c.value +=1; } func (c *Counter) Get()int{ if c.mutex!=nil{ c.mutex.Lock();defer c.mutex.Unlock(); }else{ c.rwmtx.RLock();defer c.rwmtx.RUnlock(); } return c.value ; } ``` 在这个例子中定义了一个简单的计数器类,根据初始化参数决定是否启用 RW 版本的控制逻辑。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luyaran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值