您的位置:首页 > 其它

自己实现一个简单的读写锁

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源代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: