go中的sync.mutex与RWMutex

1.sync.mutex的结构

强调:mutex使用之后不要做copy操作,锁拷贝—使用vet工具检测。

1.1sync.mutex数据结构

type Mutex struct {
	    state int32  //状态
    	sema  uint32  //信号量,用于控制锁的获得和释放(等待队列)
    }

在sync.Mutex的实现中,sema是一个信号量,用于控制锁的获取和释放。它是一个uint32类型的字段,初始值为0。

1.2sync.mutex具体实现

sync.mutex
locked:互斥锁的锁定状态

woken:从正常模式中被唤醒

starving : 饥饿模式

waitershift(剩下的位置):等待者数量

为了保证锁的公平性,设计上互斥锁有两种状态:正常状态和饥饿状态。

1.正常模式流程:

①当一个协程需要获取锁时,它会先通过CAS原子操作将sema值加1。如果加1后的结果为1,表示锁之前是未锁定状态,该线程可以直接获取锁;

②如果加1后的结果大于1,表示锁之前已经被其他协程锁定了,会进行多次自旋尝试。如果自旋失败该协程会进入等待队列并被阻塞。

③.当一个协程释放锁时,它会通过原子操作将sema值减1,并唤醒等待队列中的一个协程。

如果一个等待的协程超过1ms没有获得锁,那么就会把锁转为饥饿模式。

2.饥饿模式下,锁的所有权将从 unlock 的 gorutine 直接交给交给等待队列中的第一个。新来的 goroutine 将不会尝试去获得锁,即使锁看起来是 unlock 状态, 也不会去尝试自旋操作,而是放在等待队列的尾部。

饥饿模式的优点:能能够减少锁自旋的竞争,并且尽量的保持公平。

CAS是什么?

CAS(Compare and
Swap)原子操作是一种并发编程中常用的原子操作。它可以在多线程环境下保证变量的原子性操作,从而避免并发问题。CAS操作包含三个参数:内存地址V、旧值A、新值B。当V的值等于A时,CAS会将V的值更新为B。如果V的值不等于A,说明在CAS操作过程中V已经被其他线程修改了,CAS操作不会执行任何操作。CAS操作是一种无锁算法,它比传统的锁算法更加高效,因为它不需要获取锁来保证操作的原子性。

2.读写锁RWMutex

读写锁是在出现大量的并发读、少量的并发写并且有强烈的性能需求时,对于mutex做出的优化。

2.1.读写锁与读者写者模型

读者写者模型

2.2RWMutex底层结构示意图:

在这里插入图片描述
2.读锁状态下:可以有多个协程同时获取读锁,

读锁实现

①比较readCount>0 直接+1,如果readCount<0 则说明写锁在排队,readerWait++。

②当一个读者读完的时候,readCount-1,readerWait-1,如果readerWait==0则说明是最后一个读者,可以使写进程执行写操作。

③写锁状态下(readerCount<0):只能有一个协程获取写锁,其他协程不能获取读锁或者写锁。

写锁实现

①先通过原子操作获取写锁,如果当前没有协程持有读写锁,则可以写。否则需要进入等待队列等待读锁和写锁的释放。

②readerCount变为超级大的负值,阻塞读,并且告诉后面的读进程有写进程等待。

③等待先置协程结束,进行读操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

乔可南-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值