读写锁很像一个互斥量,他阻止多个线程同时修改共享数据的另一种方法,区分不同互斥量的是他是分读数据和写数据,一个读写锁允许同时多个线程读数据,只要他们不修改数据。
- 只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁这个其实可以很好理解,因为单纯的读操作不改变访问的任何资源,多个线程都可以同时无影响的读取资源。
- 只有读写锁处于不加锁状态时,才能进行写模式下的加锁
读写锁也称为共享-(shared-exclusive)独占锁,当读写锁以读模式加锁时,它是以共享模式锁住(即任何访问者都可以使用),当以写模式加锁时,它是以独占模式锁住(只有获得锁的人可以使用)
当一个写锁被释放时,如果读数据写数据同时等待,读数据有优先访问权限。
读写锁的类型是在内存中分配的,当读写锁是在单个进程内的各个线程共享时(默认情况),这些变量可以在进程内;当这些变量是在某个共享内存区的进程间共享时(假设指定PTHREAD_PROCESS_SHARED),这些变量在共享内存中
读数据的频率远大于写数据的频率的应用中。这样可以在任何时刻运行多个读线程并发的执行,给程序带来了更高的并发度。
与互斥锁的区别
- 互斥锁会把试图进入已保护的临界区的线程都阻塞,即同时只有一个线程持有锁
- 读写锁会根据当前进入临界区的线程是读还是写的属性来判断是否允许线程进入。
- 多个读者可以同时持有锁
初始化和销毁
/* 用于初始化读写锁,并可以设置读写锁属性, * 一般默认为NULL,如果要修改写锁属性可以详细参考链接 * http://docs.oracle.com/cd/E19455-01/806-5257/6je9h032t/index.html * PTHREAD_RWLOCK_INITIALIZER宏定义可以用来静态的分配初始一个读写锁,不需要错误检查,分配默认 * 的读写锁属性,和pthread_rwlock_init()指定NULL属性的效果是一致的。pthread_rwlock_destroy() * 用于销毁读写锁。 */ pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; int pthread_rwlock_init(pthread_rwlock_t * restrict lock,const pthread_rwlockattr_t * restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *lock);
init返回值
- 成功返回0
- [EAGAIN]:系统缺少资源来初始化读写锁
- [EBUSY]:尝试初始化一个已经初始化但还未销毁的锁.
- [EINVAL]:attr不可用.
- [ENOMEM]:内存资源不足
destroy返回值
- 成功返回0
- [EBUSY] 尝试销毁一个还锁着的锁
- [EINVAL] 指定的锁参数不可用
读加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *lock); int pthread_rwlock_timedrdlock(pthread_rwlock_t * restrict lock,const struct timespec * restrict abstime); int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock); /* 读写锁读加锁有3个函数接口,pthread_rwlock_rdlock()请求一个读锁, * 如果当前没有写线程持有锁或者没有写线程阻塞在取锁时则可以立刻获取到锁。否则请求锁的线程阻塞等 * 待,pthread_rwlock_timedrdlock() 执行同样的读加锁操作,只是有个超时时间,在指定abstime时间 *(超时指定的是绝对时间,不是相对时间)获取不到直接返回,其中abstime的类型为struct timespec */ struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; /* pthread_rwlock_tryrdlock()执行同样加锁操作的非阻塞函数,获不到锁立即返回。 注:一个线程可能获取到多个并发的读锁,如果是这样的话,对每个加锁都要释放一次。 */
成功均返回0 失败 1.pthread_rwlock_tryrdlock():[EBUSY]其写锁正被持有或者写锁阻塞等待 2.pthread_rwlock_timedrdlock():[ETIMEDOUT]加锁超时 3.pthread_rwlock_rdlock(), pthread_rwlock_timedrdlock(), and pthread_rwlock_tryrdlock():[EAGAIN]获取锁的数量已经超过最大值,[EDEADLK]当前线程已经持有写锁,[EINVAL]指定的锁参数不可用
写加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *lock); //阻塞的形式获取一个写锁 int pthread_rwlock_timedwrlock(pthread_rwlock_t * restrict lock,const struct timespec * restrict abstime); //执行相同的取写锁的操作, 只是有个超时时间,在指定abstime绝度时间内获取不到直接返回 int pthread_rwlock_trywrlock(pthread_rwlock_t *lock); //执行同样加锁操作的非阻塞函数,获不到锁立即返回。
成功均返回0 失败
1.The pthread_rwlock_trywrlock():[EBUSY]不能非阻塞的获得锁 2.pthread_rwlock_timedwrlock():[ETIMEDOUT]加锁超时 3.pthread_rwlock_wrlock(), pthread_rwlock_timedwrlock(), and pthread_rwlock_trywrlock():[EDEADLK]调用的线程已经持有读写锁了,[EINVAL]指定的锁参数不可用
解锁
int pthread_rwlock_unlock(pthread_rwlock_t *lock);// 改函数用来释放获取到的读写锁。
成功均返回0 失败 1.[EINVAL]指定的锁参数不可用 2.[EPERM]当前线程没有持有锁
用互斥量实现读写锁
//rwlock.h #include <pthread.h> typedef struct rwlock_tag { pthread_mutex_t mutex; pthread_cond_t r;//读存取 pthread_cond_t w;//写存取 int valid;//检测普通的语法错误,如试图加锁一个没有初始化的读写锁 int r_active;//读线程活跃 int w_active;//写线程活跃 int r_wait;//读线程等待数 int w_wait;//写线程等待数 }rwlock_t; #define RWL_INITIALIZER \ { \ PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER, \ PTHREAD_COND_INITIALIZER,RWLOCK_VALID,,,, \ } #define RWLOCK_VALID 0xfacade int rwl_init(rwlock_t* rwlock); int rwl_destroy(rwlock_t* rwlock); int rwl_readlock(rwlock_t* rwlock); int rwl_readtrylock(rwlock_t* rwlock); int rwl_readunlock(rwlock_t* rwlock); int rwl_writelock(rwlock_t* rwlock); int rwl_writetrylock(rwlock_t* rwlock); int rwl_writeunlock(rwlock_t* rwlock);
/* *rwlock.c *实现了rwlock.h中的各个函数 */ #include <pthread.h> #include "errors.h" #include "rwlock.h" int rwl_init(rwlock_t *rwl)//初始化读写锁 { rwl->r_active=rwl->w_active=; rwl->r_wait=rwl->w_wait=; int status=pthread_mutex_init(&rwl->mutex,NULL); if(status) return status; status=pthread_cond_init(&rwl->r,NULL); if(status) { pthread_mutex_destroy(&rwl->mutex); return status; } status=pthread_cond_init(&rwl->w,NULL); if(status) { pthread_cond_destroy(&rwl->r); pthread_mutex_destroy(&rwl->mutex); return status; } rwl->valid=RWLOCK_VALID; ; } int rwl_destroy(rwlock_t *rwl)//释放读写锁 { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; //check whether any threads own the lock or //whether any thread known to be waiting ||rwl->w_active>)|| (rwl->r_wait>||rwl->w_wait>)) { pthread_mutex_unlock(&rwl->mutex); return EBUSY; } rwl->valid=; status=pthread_mutex_unlock(&rwl->mutex); if(status) return status; status=pthread_mutex_destroy(&rwl->mutex); int status1=pthread_cond_destroy(&rwl->r); int status2=pthread_cond_destroy(&rwl->w); ?status:(status1!=?status1:status2); } //cleanup when the reads clock condition variable wait canceled. //record that the thread is no longer waiting,and unlock the mutex. static void rwl_readcleanup(void *arg)//read/write is a cancel point { rwlock_t *rwl=(rwlock_t*)arg; rwl->r_wait--; pthread_mutex_unlock(&rwl->mutex); } //cleanup when the write lock condition variable wait is canceled. //record that the thread is no longer waiting and unlock the mutex. static void rwl_writecleanup(void* arg) { rwlock_t* rwl=(rwlock_t*)arg; rwl->w_wait--; pthread_mutex_unlock(&rwl->mutex); } //lock a reads/write for reads access. write thread priority. this is very important!!! int rwl_readlock(rwlock_t* rwl) { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; if(rwl->w_active) { rwl->r_wait++; pthread_cleanup_push(rwl_readcleanup,(void*)rwl); while(rwl->w_active) { status=pthread_cond_wait(&rwl->r,&rwl->mutex);//wait reads if(status) return status; } pthread_cleanup_pop(); } ) rwl->r_active++; pthread_mutex_unlock(&rwl->mutex); return status; } int rwl_readtrylock(rwlock_t* rwl)//attempt to lock a read/write lock for read access { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; if(rwl->w_active) status=EBUSY; else rwl->r_active++; int status1=pthread_mutex_unlock(&rwl->mutex); ?status1:status; } int rwl_readunlock(rwlock_t* rwl) { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; rwl->r_active--; &&rwl->w_wait>) status=pthread_cond_signal(&rwl->w);//signal write int status1=pthread_mutex_unlock(&rwl->mutex); ?status:status1; } int rwl_writelock(rwlock_t* rwl) { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; ) { rwl->w_wait++; pthread_cleanup_push(rwl_writecleanup,(void*)rwl); ) { //wait write status=pthread_cond_wait(&rwl->w,&rwl->mutex); if(status) break; } pthread_cleanup_pop(); rwl->w_wait--; } ) rwl->w_active=; pthread_mutex_unlock(&rwl->mutex); return status; } int rwl_writetrylock(rwlock_t* rwl) { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; ) status=EBUSY; else rwl->w_active=; int status1=pthread_mutex_unlock(&rwl->mutex); ?status:status1; } int rwl_writeunlock(rwlock_t* rwl) { if(rwl->valid!=RWLOCK_VALID) return EINVAL; int status=pthread_mutex_lock(&rwl->mutex); if(status) return status; rwl->w_active=; ) { status=pthread_cond_broadcast(&rwl->r);//broadcast reads if(status) { pthread_mutex_unlock(&rwl->mutex); return status; } } ) { status=pthread_cond_signal(&rwl->w);//signal write if(status) { pthread_mutex_unlock(&rwl->mutex); return status; } } status=pthread_mutex_unlock(&rwl->mutex); return status; }
//main.c #include <stdio.h> #include "rwlock.h" #include "errors.h" #define THREADS 5 #define DATASIZE 15 #define INERATIONS 6 typedef struct thread_tag { int thread_num;//array index pthread_t tid; int updates; int reads; int interval;//how many times when exce write. }thread_t; typedef struct data_tag { rwlock_t lock; int data; int updates; }data_t; thread_t threads[THREADS]; data_t data[DATASIZE]; void* thread_routine(void* arg) { thread_t* self=(thread_t*)arg; ; ; ; int status; ;i<INERATIONS;++i) { ) { status=rwl_writelock(&data[j].lock); if(status) err_abort(status,"write lock"); data[j].data=self->thread_num; data[j].updates++; self->updates++; status=rwl_writeunlock(&data[j].lock); if(status) err_abort(status,"write unlok"); } else { status=rwl_readlock(&data[j].lock); if(status) err_abort(status,"reads lock"); self->reads++; if(data[j].data==self->thread_num) repeats++; status=rwl_readunlock(&data[j].lock); if(status) err_abort(status,"reads unlock"); } j++; if(j>=DATASIZE) j=; } ) printf("threads %d found unchanged elements %d times:\n", self->thread_num,repeats); return NULL; } int main() { pthread_setconcurrency(THREADS); int status; int i,j; unsigned ; ; ; //initialize the share data ;i<DATASIZE;++i) { data[i].data=; data[i].updates=; status=rwl_init(&data[i].lock); if(status) err_abort(status,"Init rwlock"); } //create threads to access shared data ;j<THREADS;++j) { threads[j].thread_num=j; threads[j].updates=; threads[j].reads=; threads[j].interval=rand_r(&seed)%; status=pthread_create(&threads[j].tid,NULL,thread_routine,(void*)&threads[j]); if(status) err_abort(status,"create thread"); } //wait for all threads to complete,and collect statistics ;j<THREADS;++j) { status=pthread_join(threads[j].tid,NULL); if(status) err_abort(status,"join threads"); thread_updates+=threads[j].updates; printf("%02d: interval %d,updates %d,reads %d\n", j,threads[j].interval,threads[j].updates,threads[j].reads); } //collect statistics for the data ;i<DATASIZE;++i) { data_updates+=data[i].updates; printf("data %02d: value %d,%d updates\n",i,data[i].data, data[i].updates); rwl_destroy(&data[i].lock); } printf("%d threads updates,%d data updates\n",thread_updates, data_updates); ; }
写一个Makefile将各个源文件一块编译
下面是执行结果: