UNP卷二 chapter8 读写锁

1、读写锁

被锁锁住的临界区域通常涉及由多个线程共享一个或多个数据的访问或更新。而有时候可以在读某个数据与修改某个数据之间区别。

读写锁将在获取读写锁用于读和获取读写锁用于写之间作区分。

i、只在没有线程持有某个给定的读写锁用于写,那么任意数目的线程可以持有该读写锁用于读;

ii、仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配该读写锁用于写。

通俗的话来讲,读写锁没有写时,无所谓多少个读;读写锁无读无写时,才可用于写

读写锁应用场景是读数据比修改数据频繁,此时可改用读写锁代替互斥锁中获益。任意给定时刻允许多个读出者存在提供了更高的并发度,同时在某个写入者修改数据期间保护该数据,以免任何其他读出者或写入者的干扰。

获取一个读写锁用于读称为共享锁,获取一个读写锁用于写称为独占锁

2、获取与释放读写锁

读写锁的数据类型为pthread_rwlock_t。如果此类型的某个变量是静态分配,可通过赋常值PTHREAD_RWLOCK_INITIALIZER初始化。

pthread_rwlock_rdlock获取一个读出锁,如果对应的读写锁已由某个写入者持有,则阻塞调用线程(即所谓有写时阻塞)。pthread_rwlock_wrlock获取一个写入锁,如果对应的读写锁已由另一个写入者持有,或者已由一个或多个读出者持有,则阻塞调用线程(即所谓有读或有写时阻塞)。pthread_rwlock_unlock释放一个读出锁或写入锁。

#include<pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr);
int pthread_rwlock_unlock(pthread_rwlock_t *rwptr);
				//均返回:若成功则为0,若出错则为正的Exxx值
以下两个函数试获取一个读出锁或写入锁,如果该锁不能马上取得,那就返回一个EBUSY错误,而不是把调用线程投入睡眠。
#include<pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwptr);
				//均返回:若成功则为0,若出错则为正的Exxx值

3、读写锁属性

第二点也讲到,读写锁变量可以通过静态分配然后初始化常值PTHREAD_RWLOCK_INITIALIZER。当然也可以动态分配得到。既然是动态分配,肯定是需要内存分配,于是可以利用pthread_rwlock_init来动态初始化。当一个线程不再需要某个读写锁时,可以调用pthread_rwlock_destroy摧毁

#include<pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwptr);
				//均返回:若成功则为0,若出错则为正的Exxx值
初始化某个读写锁时,如果attr是个空指针,那就使用默认属性。以下两函数赋予动态锁非默认属性:
#include<pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);//非默认属性初始化
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
				//均返回:若成功则为0,若出错则为正的Exxx值
数据类型为pthread_rwlockattr_t的某个属性对象一旦初始化,就通过不同的函数来启用或禁止特定属性。此处举一属性是PTHREAD_PROCESS_SHARED,对应读写锁在不同进程间共享,而不仅仅是在单个进程内的不同线程间共享。利用以下两个函数实现:
#include<pthread.h>
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *valptr);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int value);
				//均返回:若成功则为0,若出错则为正的Exxx值

利用上述三类线程函数动态分配读写锁的使用方法:

首先分配一块动态内存,利用pthread_rwlockattr_init进行属性初始化,再通过pthread_rwlockattr_setpshared或不同函数设置属性,再次通过pthread_rwlock_init函数对动态内存及所设置属性进行读写锁初始化,最后再调用pthread_rwlock_rdlock上读出锁

后续小节是利用互斥锁及条件变量实现读写锁,具体内容参考P142-P148

4、线程取消

如果pthread_rwlock_rdlock的调用线程阻塞在其中的pthread_cond_wait调用上并随后被取消,它就在仍持有互斥锁的情况下终止(但是互斥锁没有得到释放哈!!!!)。线程取消利用pthread_cancel函数,一个线程可以被同一进程内的任何其他线程所取消,而pthread_cancel的唯一参数就是取消线程的线程ID(注意:线程ID在创建线程时被告知)。

#include<pthread.h>
int pthread_cancel(pthread_t tid);
				//返回:若成功则为0,若出错则为正的 Exxx值
为处理被取消的可能情况,任何线程可以安装(压入)和删除(弹出)清理处理程序。
#include<pthread.h>
void pthread_cleanup_push(void(*function)(void*), void *arg);
void pthread_cleanup_pop(int execute);

上述函数被调用的情况如下:

i、调用线程被取(由某个线程调用pthread_cancel完成);

ii、调用线程自愿终止(或者通过调用pthread_exit,或者从自己的线程当前持有的任何互斥锁或信号量解锁)。

pthread_cleanup_push的function参数是调用线程被取消时所调用的函数的地址,arg是它的单个参数。

pthread_cleanup_pop总是删除调用线程的取消清理栈中位于栈顶的函数,而且如果execute不为0,那就调用函数。

具体实例见第15章吧!此处暂略!

以上知识点来均来自steven先生所著UNP卷二(version2),刚开始学习网络编程,如有不正确之处请大家多多指正。

猜你喜欢

转载自blog.csdn.net/tt_love9527/article/details/81054614
今日推荐