go的读写锁sync.RWMutex

有这样一个经典的读写锁问题,假设读锁和写锁之前互斥,读锁和读锁之间不互斥。现在做一个实验:

1、线程A加一个读锁 ,然后不释放;
2、然后线程B想加一个写锁,会被线程A的读锁阻塞;
3、然后有个线程C尝试去加一个读锁。

按照上面的步骤,步骤3 能加锁成功吗?

使用go语言的sync.RWMutex模拟这段代码,大概如下:

func main() {

	var flag int
	var rwLock sync.RWMutex

	go func() { //sessionA
		rwLock.RLock() //加读锁
		fmt.Println("session A", "flag=", flag)
	}()

	go func() { //sessionB
		time.Sleep(time.Second * 5)
		fmt.Println("sessionB try to get write lock")
		rwLock.Lock() //加写锁
		flag = 10
		fmt.Println("session B", "flag=", flag)
	}()

	go func() { //sessionC
		time.Sleep(time.Second * 8)
		fmt.Println("sessionC try to get read lock")
		rwLock.RLock() //加读锁
		fmt.Println("session C", "flag=", flag)
	}()

	time.Sleep(time.Second * 15)
	fmt.Println("end")

}

执行下上面的代码,我们会发现得出下面这样的结果。

很明显,线程B是没有上锁成功的,因为flag最后的值没有变,还是0;线程C也是没有上锁成功的,因为线程C里面的内容 fmt.Println("session C", "flag=", flag) 没有执行成功。然后问题来了,发现了吗,线程B明明没有上写锁成功啊,为什么我的线程C就是上不了锁啊?毕竟线程A的读锁也不会和我的读锁互斥啊

下面一段读写锁的概念和图都转载自 小林coding的文章,原文请点击这里

读写锁根据实现的不同,可以分为「读优先锁」和「写优先锁」。

读优先锁期望的是,读锁能被更多的线程持有,以便提高读线程的并发性,它的工作方式是:当读线程 A 先持有了读锁,写线程 B 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 C 仍然可以成功获取读锁,最后直到读线程 A 和 C 释放读锁后,写线程 B 才可以成功获取读锁。如下图:

而写优先锁是优先服务写线程,其工作方式是:当读线程 A 先持有了读锁,写线程 B 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 C 获取读锁时会失败,于是读线程 C 将被阻塞在获取读锁的操作,这样只要读线程 A 释放读锁后,写线程 B 就可以成功获取读锁。如下图:

看了上面读优先锁和写优先锁的解释,我们可以明白,原来go中sync.RWMutex的实现,是基于写优先原则去实现的,线程C上不了锁就是因为它前面还有个线程B的写锁在等待,而写锁是优先于读锁的。

看了go的sync.RWMutex实现,那么其他的锁也是这么设计的吗?让我们一起去看看MySQL的MDL锁是怎么实现的?

一起做个实验,假设有一张student表长下面这样:

然后我们做下面几个操作:
1、线程A开启事务,然后查询student表,正常返回结果;

2、线程B查询student表,也是正常返回结果;

3、线程C给student表添加字段,被阻塞了。这是因为MySQL中是有MDL(元数据)锁的,当对一个表做增删改查操作的时候,加 MDL读锁;当要对表做结构变更操作的时候,加 MDL 写锁。而写锁和读锁是互斥的,所以这里线程C想获取写锁,但被线程A的读锁阻塞了。

4、线程D再次执行查询语句,发现这时也被阻塞了。

到这里,我们应该也不难发现,MySQL的MDL锁也是写优先锁。其实也很好理解,像上面这个例子,我们在线程C中想加入一个字段s_adress,那么后面的线程D可能就是希望能查出这个新字段的。所以此时线程D被阻塞了,等线程C先执行完。

以上就是从go的读写锁引和MySQL的MDL锁结合在一起,对读写锁的一些思考和研究。我们知道了原来读写锁还有更细的划分 “读优先锁”和“写优先锁”。留个小问题,大家可以去测下MySQL中的行锁,即 FOR UPDATE (写锁)和 LOCK IN SHARE MODE(读锁) ,看下是读优先还是写优先呢?

posted @ 2024-08-18 22:19  snail_lie  阅读(30)  评论(0编辑  收藏  举报