自己实现一个简单的读写锁
2017-11-14 14:19
399 查看
自己实现一个简单的读写锁
================================================1.读写锁的定义
读写锁与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区,读写锁比它有更高的并行性。读写锁有以下特点:1.如果一个线程用读锁锁定了临界区,那么其他线程也可以用读锁来进入临界区,这样就可以多个线程并行操作。但这个时候,如果再进行写锁加锁就会发生阻塞,写锁请求阻塞后,后面如果继续有读锁来请求,这些后来的读锁都会被阻塞!这样避免了读锁长期占用资源,防止写锁饥饿!
2.如果一个线程用写锁锁住了临界区,那么其他线程不管是读锁还是写锁都会发生阻塞!
2.读写锁的函数接口
1.创建读写锁1.1:宏常量初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
1.2:函数初始化
#include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
rwlock:读写锁的pthread_rwlock_t结构指针
attr:读写锁的属性结构指针。不需要别的属性默认为NULL。
2.读写锁加锁与解锁
#include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
rwlock:创建的读写锁指针
3.其他类型的加锁
#include <pthread.h> #include <time.h> int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout); int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);
try类函数加锁:如果获取不到锁,会立即返回错误EBUSY!
timed类函数加锁:如果规定的时间内获取不到锁,会返回ETIMEDOUT错误!
4.销毁读写锁
#include <pthread.h> int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
如果在加锁状态,将返回一个错误。
3.实现一个简单的读写锁。
1.读写锁用到的数据结构typedef struct { pthread_mutex_t rw_mutex; //basic lock for protect struct pthread_cond_t rw_condreaders; //for reader waiting pthread_cond_t rw_condwriters; //for writer waiting int rw_magic; //ues for error checking int rw_nwaitreaders; //the munber of waiting int rw_nwaitwriters; int rw_refcount; //-1 if wait has lock else rw_refcount = 0 no one has lock eles >0 read has lock }my_pthread_rwlock_t;
2.其他的宏定义
#define RW_MAGIC 0x20171104 //ues for error checking #define MY_PTHREAD_RWLOCK_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,\ PTHREAD_COND_INITIALIZER,\ PTHREAD_COND_IN e5f7 ITIALIZER,\ RW_MAGIC,0,0,0} //use for init typedef int my_pthread_rwlockattr_t;
3.pthread_rwlock_init函数
int my_pthread_rwlock_init(my_pthread_rwlock_t *rw, my_pthread_rwlockattr_t *attr) { int result; if (attr != NULL) return EINVAL; if ((result = pthread_mutex_init(&rw->rw_mutex, NULL)) != 0) { return result; } if ((result = pthread_cond_init(&rw->rw_condreaders,NULL)) != 0) { pthread_mutex_destroy(&rw->rw_mutex); return result; } if ((result = pthread_cond_init(&rw->rw_condwriters,NULL)) != 0) { pthread_cond_destroy(&rw->rw_condreaders); pthread_mutex_destroy(&rw->rw_mutex); return result; } rw->rw_nwaitreaders = 0; rw->rw_nwaitwriters = 0; rw->rw_refcount = 0; rw->rw_magic = RW_MAGIC; }
在初始化的读写锁的时候,如果初始化失败,要将之前初始化的锁摧毁,防止再次初始化出错!。
4.pthread_rwlock_destroy
int my_pthread_rwlock_destroy(my_pthread_rwlock_t *rw) { if (rw->rw_magic != RW_MAGIC) return EINVAL; if (rw->rw_refcount != 0 || rw->rw_nwaitreaders != 0|| rw->rw_nwaitwriters != 0) return EBUSY; pthread_mutex_destroy(&rw->rw_mutex); pthread_cond_destroy(&rw->rw_condreaders); pthread_cond_destroy(&rw->rw_condwriters); rw->rw_magic = 0; return 0; }
在这里的rw_magic这个成员用于识别这个锁是否被摧毁或初始化过。另一方面,在加锁和有等待加锁的线程的时候,禁止摧毁锁。
5.读写锁
int my_pthread_rwlock_rdlock(my_pthread_rwlock_t *rw) { int result; if(rw->rw_magic != RW_MAGIC) return EINVAL; if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0) return result; #ifdef RD_LOCK while(rw->rw_refcount<0) { rw->rw_nwaitreaders++; result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex); rw->rw_nwaitreaders--; if(result != 0) break; } if(result == 0) rw->rw_refcount++; #endif #ifdef WR_LOCK while (rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0) { rw->rw_nwaitreaders++; result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex); rw->rw_nwaitreaders--; if (result != 0) break; } if (result == 0) rw->rw_refcount++; #endif pthread_mutex_unlock(&rw->rw_mutex); return result; } int my_pthread_rwlock_wrlock(my_pthread_rwlock_t *rw) { int result; if(rw->rw_magic != RW_MAGIC) return EINVAL; if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0) return result; #ifdef RD_LOCK while(rw->rw_refcount != 0 || rw->rw_nwaitreaders != 0) { rw->rw_nwaitwriters++; result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex); rw->rw_nwaitwriters--; if(result != 0) break; } if(result == 0) rw->rw_refcount = -1; #endif #ifdef WR_LOCK while(rw->rw_refcount != 0) { rw->rw_nwaitwriters++; result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex); rw->rw_nwaitreaders--; if(result != 0) break; } if (result == 0) rw->rw_refcount = -1; #endif pthread_mutex_unlock(&rw->rw_mutex); return result; }
在这里面我给出的两种模式,一种是读锁优先,一种是写锁优先。
对于读锁优先来说。在分配锁的时候,应该优先分配读锁,而写锁必须等待所有的读锁退出后才能分配锁。
另一方面,读锁可以无上限的分配,写锁只能有一个。核心代码如下
#ifdef RD_LOCK while(rw->rw_refcount<0) //当rw_refcount < 0 代表分配了写锁,所以必须等待写锁释放后才能分配读锁 { rw->rw_nwaitreaders++; result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex); //在这里等待写锁的释放。 rw->rw_nwaitreaders--; if(result != 0) //result != 0 时候代表出错。 break; } if(result == 0) rw->rw_refcount++; #ifdef RD_LOCK while(rw->rw_refcount != 0 || rw->rw_nwaitreaders != 0) //只有当没有等待分配的读锁和已经分配的锁的时候才可以分配读锁 { rw->rw_nwaitwriters++; result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex); //在这里等待读锁的释放 rw->rw_nwaitwriters--; if(result != 0) break; } if(result == 0) rw->rw_refcount = -1;
写锁优先的可以参照我上面给出的写锁优先宏下面的代码看一看。在此不在赘述。
6.解锁函数
int my_pthread_rwlock_unlock(my_pthread_rwlock_t *rw) { int result; if(rw->rw_magic != RW_MAGIC) return EINVAL; if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0) return result; if(rw->rw_refcount > 0) rw->rw_refcount--; else if(rw->rw_refcount == -1) rw->rw_refcount = 0; else printf("unlock rwlock error: rw_refcount = %d\n",rw->rw_refcount); #ifdef RD_LOCK if (rw->rw_nwaitreaders > 0) { result = pthread_cond_broadcast(&rw->rw_condreaders); } else if (rw->rw_nwaitwriters > 0) { if (rw->rw_refcount == 0) { result = pthread_cond_signal(&rw->rw_condwriters); } } #endif #ifdef WR_LOCK if(rw->rw_nwaitwriters > 0) { if(rw->rw_refcount == 0) { result = pthread_cond_signal(&rw->rw_condwriters); } } else if(rw->rw_nwaitreaders > 0) { result = pthread_cond_broadcast(&rw->rw_condreaders); } #endif pthread_mutex_unlock(&rw->rw_mutex); return result; }
在实现的解锁的时候,要形成临界区域,防止因为线程竞争而导致的未知错误。
以读锁优先为例,释放锁后,应该先激活等待分配的读锁,如果没有等待分配的读锁,再激活等待分配的写锁(如果此时rw_refcount == 0 )。
7.线程取消可能会带来的影响。
有时候在等待分配读锁/写锁的线程在等待的过程中,被取消了,就会导致由互斥量而引起的死锁或者由于计数不正确导致的死锁。
void clean_up(void *arg) { my_pthread_rwlock_t *rw = (my_pthread_rwlock_t*)arg; rw->rw_nwaitreaders--; pthread_mutex_unlock(&rw->rw_mutex); } ... if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0) return result; #ifdef RD_LOCK while(rw->rw_refcount<0) { rw->rw_nwaitreaders++; pthread_cleanup_push(clean_up, rw); result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex); pthread_cleanup_pop(0); rw->rw_nwaitreaders--; if(result != 0) break; } if(result == 0) rw->rw_refcount++; #endif
如果线程被取消就用clean_up函数回复在加锁之前的现场!
8.源代码
Github源代码
相关文章推荐
- java中自己实现一个服务端对应多个客户端的简单代码
- 自己实现一个简单的网购秒杀系统
- Spring Boot 揭秘与实战 自己实现一个简单的自动配置模块
- 自己实现一个最简单的数据库
- 自己动手实现一个简单的string类(三)
- 一个简单地list侧滑菜单,自己实现不是梦
- 自己动手系列——实现一个简单的LinkedLis
- 自己用 Netty 实现一个简单的 RPC
- 自己动手系列——实现一个简单的LinkedList
- 自己动手系列——实现一个简单的LinkedLis
- 将十进制整形数转换成二进制,然后通过字符型输出 自己实现的一个简单的例子
- python装饰器,自己实现一个简单的装饰器
- 自己实现的一个简单的相册效果
- 自己实现一个简单的ArrayList
- 使用hadoop命令rcc生成Record 一个简单的方法来实现自己的定义writable对象
- 【NROS-00】自己实现一个简单操作系统
- 自己实现的一个简单的HttpEngine库
- 一个自己实现的简单的智能指针模板类
- 模拟一个自己的jquery(二) 简单实现$
- 自己动手实现一个简单的 IOC