PostgreSQL数据库锁机制——自旋锁浅析

什么是自旋锁
大明说:“并发控制的部分比较繁杂,我们今天主要关注锁的实现,不过我们先从最底层的部分开始,先来看一看是如何实现一个自旋锁的,你知道自旋锁吗?” 小明感觉这个概念好像在哪里听过,但又没什么印象,于是小声的说:“不知道 。”
大明哈哈一笑,嘲讽道:“这么轻易就说不知道?自信一点!大胆一点!再问你一遍,知道不知道自旋锁?!” 小明感觉受到了很大的鼓舞,于是大声的说:“我不知道!”
“嗯,很自信嘛 。。。来,让朕给你讲讲 。的锁的概念有很多种,比如常见的表锁、行锁、页锁、轻量锁、自旋锁等等,这里面最底层的是实现是自旋锁,它和硬件直接接触,并且屏蔽各种不同硬件和操作系统的细节,通常利用硬件提供的原子操作指令来实现 。”
“那为什么要使用这种锁呢?”
“自旋锁是一种互斥锁,它通常是用来保护临界区,这种保护的方式是一种‘不是你死就是我活’的方式,比如说我通过锁获取到这个临界区的访问权限,那么其他人就必须等待,那它怎么等待呢?”
“我知道!”小明抢答道:“如果干等着的话,CPU就不能被充分的利用,所以我知道系统里有一个sleep函数能很好的解决这个问题,如果我们不能获取到锁,那么我们可以睡一会,再来获取锁,我听说sleep函数可以释放CPU资源,这样其他进程或者线程就能充分的利用CPU了,这样CPU即被充分的利用,我也能不停的对锁发出请求,岂不是两全其美 。”小明伸了伸衣袖,对大明说:“拿笔来!让我展示一下的能力 。”
大明给他找了纸和笔,调侃道“看上去很高调嘛?” “我也想低调,但是实力不允许啊!”小明一边说,一边在纸上写出了自己的蓝图:
while(!spinlock_aquire(&lock))sleep(100);
大明接过笔在小明写的代码上画了一个大大的对勾,对小明的行为表示肯定,然后又画了一个X,解释道:“虽然看上去合理,但是这里存在一个问题,有时我们要保护的临界区只有区区几个指令,锁的持有者实际上占有锁的时间是极短的,换句话说锁的请求者实际上不用等太长时间就能获得锁,这时候就需要考虑这个睡眠是不是合适了 。”
小明说:“我读书少,你别骗我,有什么不合适的?不睡眠难道一直占着CPU资源不放,浪费CPU资源?这简直是占着茅坑XXX嘛 。”
对,就是占着茅坑XXX,你说的睡眠模式实际上进行了CPU上下文的切换,但这种切换需要非常多的时钟周期,如果我们要保护的临界区很短,这种切换的代价就显得有点大了,所以释放CPU资源还不如占着好 。”
小明想了想,说:“哦,那这种锁有点像一个小朋友遇到了想要的玩具,家长不给买的时候的行为 。。。”
大明笑着说:“是的,你小时候就是这样,而且,你在地上打滚的时候从来不休息,一直处于滚动的状态,所以你很小的时候就理解了自旋锁的本质,就是忙等 。”
自旋锁的伪码
小明继续说:“这样的话,自旋锁的实现就比较容易了,也就是不sleep嘛,我来把它写出来 。”说着开始在纸上开始写自旋锁实现的伪码,因为已经了解了自旋锁的基本原理,所以写代码的过程是极为顺利的,小明一边写一边暗暗佩服自己:都是九年义务教育,怎么我就这么优秀呢,像我这样优秀的人,本该灿烂过一生啊 。让我们假设lock参数是一个线程或进程共享变量 。
int spinlock_acquire(int *lock) {while(*lock != 0)continue;*lock= 1;return *lock;}int spinlock_release(int *lock) {*lock= 0;}