读写锁

读写锁规则:

1.只要没有线程在修改某个给定的数据,那任意数目的线程都可以拥有该数据的读访问权限。
2.仅当没有其他线程在读或修改某个给定的数据时,当前线程才可以修改它。

读写锁优点:

1.某些应用中读数据比写数据频繁,则可以改用读写锁替代互斥锁。
2.任意给定时刻允许多个读出者存在提供了更高的并发度。

共享-独占上锁:

对于某个给定资源的共享访问。

共享锁:

获取一个读写锁用于读。

独占锁:

获取一个读写锁用于写。


pthread_rwlock_t 数据类型:

只需要互斥锁和条件变量就能实现读写锁。


typedef struct{
pthread_mutex_t rw_mutex;
pthread_cond_t rw_condreaders;//读线程等待
pthread_cond_t rw_condwriters;//写线程等待
int rw_magic;
int rw_nwaitreaders;//读等待个数
int rw_nwaitwriters;//写等待个数
int rw_refcount;//读写锁当前状态:-1表示它是一个写入锁,0表示可用,正值表示当前容纳那么多读出锁。
}pthread_rwlock_t;

读写锁初始化:
int pthread_rwlock_init(pthread_rwlock_t *rwptr,const pthread_rw_lockattr_t *attr)//动态初始化读写锁变量,若attr指针为空,则使用默认属性。
{
	int result;
	if(attr != NULL)
		return(EINVAL);//不支持函数给读写锁赋属性(仅使用默认属性)
	if((result = pthread_mutex_init(&rw->rw_mutex),NULL)) != 0)//使用互斥锁和条件变量实现读写锁
		goto err1;
	if((result = pthread_cond_init(&rw->rw_condreaders,NULL)) != 0)//使用互斥锁和条件变量实现读写锁
		goto err2;
	if((result = pthread_cond_init(&rw->rw_condwriters,NULL)) != 0)
		goto err3;
	rw->rw_nwaitreaders = 0;
	rw->rw_nwaitwriters = 0;
	rw->rw_refcount = 0;
	rw->rw_magic = RW_MAGIC;//表示该结构已经初始化完毕。
	
	return(0);
	
	err3:
		pthread_cond_destory(&rw->rw_condreaders);
	err2:
		pthread_mutex_destory(&rw->rw_mutex);
	err1:
		return(result);
}


读写锁摧毁:
int pthread_rwlock_destory(pthread_rwlock_t *rwptr)//当所有线程都不需要某个读写锁时,则摧毁它。
{
	if(rw->rw_magic != RW_MAGIC)//还未被初始化。
		return(EINVAL);
	if(rw->rw_refcount != 0 || rw->rw_nwaitreaders != 0 || rw->rw_condwriters != 0)//读写锁在使用中,则不允许摧毁。
		return EBUSY;
	
	pthread_mutex_destory(&rw->rw_mutex);//摧毁互斥锁
	pthread_cond_destory(&rw->rw_condreaders);//摧毁条件变量
	pthread_cond_destory(&rw->rw_condwriters);
	rw->rw_magic = 0;//各项已经摧毁标志
	
	return 0;
	
	
}




获取与释放读写锁:
#include 
int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr)//获取一个读出锁,若对应的读写锁已由某个写入者持有,则阻塞调用线程。
{
	int result;
	if(rw->rw_magic != RW_MAGIC)//未初始化
		return EINVAL;
		
	if((result = pthread_mutex_lock(&rw->rw_mutex)) !=0 )//上锁
		return result;
	
	while(rw->rw_refcount < 0 || rw->rw_nwaitreaders > 0)//当前有一个写入者持有读写锁,或者 有线程等待获取该读写锁的一个写入锁。则读者无法立即获得该锁。
	{
		rw->rw_nwaitreaders++;//由于无法立即获得该锁,则读者数量增加
		result = pthread_cond_wait(&wr->rw_condreaders,&rw->rw_mutex);//读者阻塞等待该锁释放
		rw->rw_nwaitreaders--;//等待结束出来,读者数量减少
		if(result != 0)
			break;
	}
	if(result == 0)
		rw->rw_refcount++;
	
	pthread_mutex_unlock(&rw->rw_mutex);//互斥锁释放
	return result;
}


int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr)//尝试获取读锁,若不能立即获得,则返回BUSY错误,而不是把调用线程投入睡眠。
{
	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_nwaitwriters > 0) //若有写入者持有该读写锁,或者 有其他线程在等待该读写锁的一个写入锁,则返回错误。
		result = EBUSY;                                //忙则立即退出,不阻塞等待。
	else
		rw->rw_refcount++;
		
	pthread_mutex_unlock(&rw->rw_mutex);
	
	return result;
}


int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr)//获取一个写入锁,若对应的读写锁已由另一个写入者持有,或者已由一个或多个读出者持有,那就阻塞调用线程。
{
	int result;
	if(rw->rw_magic != RW_MAGIC) //各项未初始化
		return EINVAL;
	if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)//进行上锁
		return result;
		
	while(rw->rw_refcount != 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; //写者占有读写锁,则将rw_refcount 置为-1.
	
	pthread_mutex_unlock(&rw->rw_mutex);
	
	return result;

}



int pthread_rwlock_tryrwlock(pthread_rwlock_t *rwptr)//尝试获取写锁,若不能立即获得,则返回BUSY错误,而不是把调用线程投入睡眠。
{
	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)//若读写锁当前不可用,则立即返回忙。
		result = EBUSY;
	else	
		rw->rw_refcount = -1;//读写锁可用,被写者占有,则置-1.
	
	pthread_mutex_unlock(&rw->rw_mutex);
	
	return result;
	
}



int pthread_rwlock_unlock(pthread_rwlock_t *rwptr)//释放一个读出锁或写入锁。
{
	int result;
	if(rw->rw_magic != RW_MAGIC)
		return result;
		
	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_refcount设置为可用。释放唯一的写者。
		rw->rw_refcount = 0;
	else	
		printf("rw_refcount = %d\n",rw->rw_refcount);
		
	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);//则进行广播唤醒所有读者线程。
		
	pthread_mutex_unlock(&rw->rw_mutex);
	return result;
}


线程取消:
若 pthread_rwlock_rdlock的调用线程阻塞在其中的pthread_cond_wait调用上并随后被取消,则在仍然持有互斥锁的情况下终止。
通过由对方调用函数pthread_cancel,一个线程可以被同一个进程中的所有其他线程取消。

int pthread_cancel(pthread_t tid);//tid待取消的线程ID

测试程序:

#include "unpipc.h"

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
pthread_t tid1,tid2;

int main(int argc,char **argv)
{
	void *status;
	set_concurrency(2);
	pthread_create(&tid1,NULL,thread1,NULL);
	sleep(1);								//创建thread1之后休眠1秒,让其获得读锁。
	pthread_create(&tid2,NULL,thread2,NULL);
	
	pthread_join(tid2,&status);//等待线程2是否被取消
	if(status != PTHREAD_CANCELED)
		printf("thread2 status = %p \n",status);
	pthread_join(tid1,&status);//等待线程1是否被取消
	if(status != NULL)
		printf("thread1 status = %p \n",status);
		
	pthread_rwlock_destory(&rwlock);//摧毁读写锁
}



void *thread1(void *arg)
{
	pthread_rwlock_rdlock(&rwlock);//线程1先获得读锁
	printf("thread1 get a read lock\n");
	sleep(3);					   //休眠,让线程2阻塞在pthread_rwlock_wrlock
	pthread_cancel(tid2);		   //取消线程2
	sleep(3);
	pthread_rwlock_unlock(&rwlock);
	return NULL;
	
}

void *thread2(void *arg)
{
	printf("thread2 trying to obtain a write lock \n");
	pthread_rwlock_wrlock(&rwlock); //由于线程1的休眠,使得线程2无法获得写锁,阻塞在此。
	printf("thread2 get a write lock\n");//由于线程1中对线程2的取消,使得下面的代码无法执行。
	sleep(1);
	pthread_rwlock_unlock(&rwlock);
	return NULL;
}

读写锁_第1张图片

你可能感兴趣的:(读写锁)