读写锁也是一种锁,与互斥锁相似,但是在互斥锁的基础上进行了改进,允许更高的并行性。互斥锁只能是加锁与不加锁状态,一次只有一个线程可以对其加锁。读写锁有三种状态,读模式下加锁,写模式下加锁,不加锁。当一个线程获得写加锁时,其他的线程仍然不能获得锁,都会被阻塞。但是当读写锁是读加锁状态,所有试图对其进行读加锁的线程仍然可以加锁,只不过对其进行写加锁的线程不会获得锁,需要等到所有的线程释放锁才可能获取到锁。(ps:当读写锁处于读加锁时,如果另有线程尝试写加锁,读写锁通常会阻塞随后的读模式请求,这样避免了读模式一直将锁占用,以致等待的写模式锁请求得不到满足。即写的优先级高:写操作被阻塞,后面读的直接阻塞,锁解开时先执行写操作)
操作:
#include<pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); //初始化,默认属性传NULL即可
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); //释放内存之前,调用该函数做清理工作,释放由init分配的资源
//成功则返回0,出错则返回错误编号
加锁与解锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //对读模式的锁加锁,获取读锁,阻塞操作
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); //对写模式的锁加锁,获取写锁,阻塞操作
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); //解锁使用同一个,释放锁
//成功则返回0,出错则返回错误编号
非阻塞的加锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); //对读模式的锁加锁,获取读锁,非阻塞操作
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); //对写模式的锁加锁,获取写锁,非阻塞操作
//成功则返回0,出错则返回错误编号
它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态(占用CPU去睡觉,其他线程不能使用CPU)。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远 高于互斥锁。
自旋锁可能存在两个问题:
1:死锁。试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。
2:过多占用cpu资源,如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试。因此,一般自旋锁实现会有一个参数限定最多持续尝试次数。超出后,自旋锁放弃,等下一次机会。
我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。
自旋锁特点:
如果进线程无法取得锁,进线程不会立刻放弃CPU时间片,而是一直循环尝试获取锁,直到获取为止。如果别的线程长时期占有锁那么自旋就是在浪费CPU做无用功,但是自旋锁一般应用于加锁时间很短的场景,这个时候效率比较高。
关于自旋锁的使用函数大家可以看这篇博客: