- 在编写多线程时,有一种情况很常见,那就是,有些公共数据修改的机会比较少,相比较改写,它们读得机会反而高得多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时较长。给这种代码段加锁,会极大地降低我们程序的效率。读写锁就是专门处理这种多读少写的情况的。
- 读写锁本质上是一种自旋锁。何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
- 举个例子来说,当我们需要长时间去等一个人的时候,我们通常采取的措施是先去做其他事情,当要等的人处理完自己的事情时,再来通知等待者。而在短时间内去等一个人时,我们就不会再去做其他事情,而是在原地等待,并且在等待期间时不时地询问要等的人还需要多久。这两种情况就正好对应了挂起等待锁和自旋锁。当预计线程等待的时间很短时,一般会采用自旋锁;当预计等待的线程时间很长时,一般会使用挂起等待锁。
- 读写锁的行为:
- 读写锁接口:
函数原型:int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
2、销毁:
函数原型:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
3、加锁:
(1)加读锁:
函数原型:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
(2)加写锁:
函数原型:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
4、解锁:
函数原型:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
- 读写锁实例:
#include <stdio.h>
#include<pthread.h>
pthread_rwlock_t rwlock;
int count = 0;
void *route_write(void *arg){
int no = (int)arg;
while(1){
int t = count;
usleep(50000);
pthread_rwlock_wrlock(&rwlock);
printf("(writer)thread(%d)id:%x,inc %d to %d\n",no,pthread_self(),t,++count);
pthread_rwlock_unlock(&rwlock);
usleep(1000000);
}
}
void *route_read(void *arg){
int no = (int)arg;
while(1){
pthread_rwlock_rdlock(&rwlock);
printf("(reader)thread(%d)id:%x,data:%d\n",no,pthread_self(),count);
pthread_rwlock_unlock(&rwlock);
usleep(20000);
}
}
int main(){
pthread_rwlock_init(&rwlock,NULL);
pthread_t tid[8];
int i = 0;
for(;i<3;i++){
pthread_create(tid+i,NULL,route_write,(void*)i);
}
for(i=3;i<8;i++){
pthread_create(tid+i,NULL,route_read,(void*)i);
}
for(i=0;i<8;i++){
pthread_join(tid[i],NULL);
}
pthread_rwlock_destroy(&rwlock);
return 0;
}
结果演示:
- 读者写者模型:
(1)读者与读者之间:共享关系;
(2)读者与写者之间:互斥且同步;
(3)写者与写者之间:互斥。
2、读者写者模型与生产者消费者模型的区别:
(1)生产者消费者模型中消费者会拿走数据;但在读者写者模型中,读者不会拿走数据。
(2)在生产者消费者模型中,消费者与消费者之间是互斥关系;但在读者写者模型中,读者与读者之间共享资源。
3、读者优先:
(1)读者、写者互斥访问文件资源;
(2)多个读者可以同时访问文件资源;
(3)只允许一个写者。
4、写者优先:
(1)写者线程的优先级高于读者线程;
(2)当没有写者到来时应该阻塞读者线程的队列;
(3)当有一个写者正在写时或在阻塞队列中时,应该阻塞读者线程的读操作,直到所有的写者线程完成写操作时,即可对读者线程解除阻塞;
(4)当没有写者线程时,读者线程应该能够同时读取文件。