linux --线程(六) 读写锁

读写者介绍

读者一写者问题是互斥问题的一个概括。一组并发的线程要访问一个共享对象,例如一个
主存中的数据结构,或者一个磁盘上的数据库。有些线程只读对象,而其他的线程只修改对象。修改对象的线程叫做写者。只读对象的线程叫做读者。写者必须拥有对对象的独占的访问,而读者可以和无限多个其他的读者共享对象。一般来说,有无限多个并发的读者和写者。
读者一写者交互在现实系统中有很多很常见的例子。例如,一个在线电影票预定系统中,允许有无限多个客户同时查看座位分配,但是正在预订座位的客户必须拥有对数据库的独占的访问。再来看另外一个例子,在一个多线程缓存Web代理中,无限多个线程可以从共享页面缓存中取出已有的页面,但是任何向缓存中写人一个新页面的线程必须拥有独占的访问。

读者—写者问题的变种问题,每个都是基于读者和写者的优先级

  • 读者优先,要求不要让读者等待,除非已经把使用对象的权限赋予了一个写者。换句话说,读者不会因为有一个写者在等待而等待。
  • 写者优先,要求一旦一个写者准备好可以写,它就会尽可能快地完成它的写操作。同上一个问题不同,在一个写者后到达的读者必须等待,即使这个写者也是在等待。

读写锁

适用的场景
少量写+大量读
与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。
读写锁的三种状态
读模式下的加锁状态
写模式下的加锁状态
不加锁的状态
加锁规则
写:

  • 一次只有一个线程可以占有写模式的读写锁即不能同时写;
  • 一个执行流进行写的时候,其他的执行流既不能写,也不能读;

读:

  • 多个线程可以同时占有读模式下的读写锁,在读写锁的内部有一个引用计数
    引用计数:标识有多少以读模式打开的读写锁
    每当打开一个以读模式的读写锁的时候,引用计数进行+ +
    每当释放一个以读模式打开的读写锁的时候,引用计数进行–
    计数变量不为零意味着现在还有线程以读模式打开读写锁。

计数变量的作用:
在于判断释放读模式打开的读写锁的,是否能够完全解锁;
如果引用计数完全减为0 ,则以读模式的读写锁就解锁了;
大于0 :否则读写锁还是以读模式打开,和想要以写模式打开进行互斥

读写锁特性
读写锁是“写模式加锁”时, 解锁前,所有对该锁加锁的线程都会被阻塞。
读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
读写锁是“读模式加锁”时, 要是既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。要是写锁优先级高,则优先满足写模式锁。
读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享

读写锁接口

设置读写优先级

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择
PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和PTHREAD_RWLOCK_PREFER_READER_NP 一致
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/
void init_rwlock()//设置读写优先级
{
#if 0 // 写优先
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_rwlock_init(&rwlock, &attr);
pthread_rwlockattr_destroy(&attr);
#else // 默认读优先,会造成写饥饿
pthread_rwlock_init(&rwlock, nullptr);
#endif
}

初始化


int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t*restrict attr);

加锁和解锁

扫描二维码关注公众号,回复: 11160866 查看本文章
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//以读模式打开(加锁)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//以写模式打开(加锁)
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁

销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

简单读写锁例子

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

volatile  int counter=20; //资源数
pthread_rwlock_t rwlock;//定义读写锁

/* 2个线程写同一全局资源,3个线程读同一全局资源 */
void *th_write(void *arg)
{
  int* i=(int*)arg;
  while (1) 
  {
    pthread_rwlock_wrlock(&rwlock);
    if(counter<=0)
    {
      pthread_rwlock_unlock(&rwlock);
      break;//终止
    }
    printf("=======writeer  %lu: writeed_counter=%d\n", pthread_self(), --counter);
    pthread_rwlock_unlock(&rwlock);
    sleep(1);

  }
  return NULL;

}


void *th_read(void *arg)
{
  int* i = (int*)arg;

  while (1)
  {
    pthread_rwlock_rdlock(&rwlock);
    if(counter<=0)
    {
      pthread_rwlock_unlock(&rwlock);
      break;//终止
    }
    
    printf("-------reader %lu: readed_counter: %d\n", pthread_self(), counter);
    pthread_rwlock_unlock(&rwlock);
    sleep(1);

  }
  return NULL;

}

int main(void)
{
  int i;
  pthread_t tid[5];
  pthread_rwlock_init(&rwlock, NULL);//初始化读写锁

  for (i = 0; i < 2;i++)
    pthread_create(&tid[i], NULL, th_write,&i);
  for (i = 2; i < 5; i++)
    pthread_create(&tid[i], NULL, th_read,&i);
  for (i = 0; i < 5; i++)
    pthread_join(tid[i], NULL);

  pthread_rwlock_destroy(&rwlock);
  return 0;

} 

怎么实现的读共享
信号量w控制对访问共享对象的临界区的访问。信号量mutex保护对共享变量(读者数量)readcnt的访问,readcnt 统计当前在临界区中的读者数量。每当一个写者进人临界区时,它对互斥锁w加锁,每当它离开临界区时,对w解锁。这就保证了任意时刻临
界区中最多只有一个写者。另一方面,只有第一个进入临界区的读者对w加锁,而只有最后一个离开临界区的读者对w解锁。当一个读者进人和离开临界区时,如果还有其他读者在临界区中,那么这个读者会忽略互斥锁w。这就意味着只要还有一个读者占用互斥锁w,无限多数量的读者可以没有障碍地进人临界区。

//这里的pv操作分别封装了 sem_wait 和 sem_post函数
int readcnt;
sem_t mutex, w; //定义信号量
void reader (void)//读模式
{
	while (1) {
		P (&mutex);
		readcnt++;
		if (readcnt == 1) //第一个进入临界区的读者对W加锁
			P (&w);
		V(&mutex);
		//读完
		P (&mutex) ;
		readcnt-- ;//读完计数减一
		if (readcnt == 0) //最后一个出临界区的读者对w解锁
			V(&w) ;
		V (&mutex) ;
}
}

读者优先级高可能导致写者饥饿(starvation), 饥饿就是一一个线程无限期地阻塞,无法进展。例如,如果有读者不断地到达,写者就可能无限期地等待。

原创文章 59 获赞 170 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_44785014/article/details/105859096