[Linux]--------读者写者模型

在之前的博客中我们说明了生产者消费者模型,在这篇博客中我们讨论类似的模型读者写者模型

概念

读者写者模型是操作系统的一种同步与互斥机制,与生产者消费者模型相似,区别在于多个读者可以共享缓冲区,但是对于写者而言相互是竞争关系,一次只能有一个写者,写入缓冲区。
在这里插入图片描述

读者写者模型具有的条件

  • 写者排他性:有多个写者的情况下,只有一个写者能够占用缓冲区。
  • 读者的并行机制:可以有多个读者同时使用缓冲区。
  • 如果写者占用缓冲区,则读者会被阻塞。

读者写者模型关系

  1. 读者优先:读者先进行读数据,写者暂时阻塞,直到缓冲区中无数据,写者访问仓库。

  2. 写者优先:写者先进行写数据,读者暂时阻塞,直到缓冲区中满数据,读者访问仓库。

  3. 公平情况:读者写者优先级相同,谁先进优先级队列,谁先运行。

读写锁

我们使用读写锁实现读者写者模型,读写锁和mutex相似,一共有三种状态,读模式下加锁,写模式下加锁,解锁。
在同一时间中,只能有一个线程申请写锁,但是有多个线程申请读锁。

读写锁状态

  1. 写加锁状态:当处于写加锁状态,所有申请锁的进程都会阻塞。
  2. 读加锁状态:处于读加锁状态,申请读模式下的锁的进程都能获得访问,但是对于申请写模式下的锁的进程受到阻塞。

当读写锁处于读模式下,有进程申请写模式的读写锁会导致,之后的申请读模式的读写锁的进程遭到阻塞,原因是防止读模式长期占用资源,导致写进程饥饿。

递归锁/非递归锁

递归锁也叫可重入锁(reentrant mutex),非递归锁也叫不可重入锁(non-reentrant mutex)。

 二者唯一的区别是:

        同一个线程可以多次获取同一个递归锁,不会产生死锁。

        如果一个线程多次获取同一个非递归锁,则会产生死锁。

读写锁接口

设置读写优先

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr,int pref);
/*
pref 共有三种参数
PTHREAD_RWLOCK_PREFER_READER_NP 读者优先,可能会导致写者饥饿。
PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但是不能递归加锁
*/

初始化

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

销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

加锁和解锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

相关案例:

//
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
using namespace std;

volatile int ticket = 1000;
pthread_rwlock_t rwlock;

class ThreadAttr
{
	pthread_t tid;
	string id;
};

void* reader(void* arg)
{
	char* id = (char*)arg;
	while (1)
	{
		pthread_rwlock_rdlock(&rwlock);
		if (tiket <= 0)
		{
			pthread_rwlock_unlock(&rwlock);
			break;
		}
		printf("%s:%d\n", id, ticket);
		pthread_rwlock_unlock(&rwlock);
		sleep(1);
	}
	return nullptr;
}




void* writer(void* arg)
{
	char* id = (char*)arg;
	while (1)
	{
		pthread_rwlock_rdlock(&rwlock);
		if (tiket <= 0)
		{
			pthread_rwlock_unlock(&rwlock);
			break;
		}
		printf("%s:%d\n", id, --ticket);
		pthread_rwlock_unlock(&rwlock);
		sleep(1);
	}
	return nullptr;
}

string create_reader_id(std::size_t i)
{
	ostringstream oss("thread reader", ios_base::ate);
	oss << i;
	return oss.str();
}

string create_writer_id(std::size_t i)
{
	ostringstream oss("thread writer", ios_base::ate);
	oss << i;
	return oss.str();
}


void init_readers(vector<ThreadAttr>& vec)
{
	for (size_t i = 0; i < vec.size(); i++)
	{
		vec[i].id = create_read_id();

		pthread_create(&vec[i].tid, nullptr, read, (void*)vec[i].id.c_str());
	}
}

void init_writers(vector<ThreadAttr>& vec)
{
	for (size_t i = 0; i < vec.size(); i++)
	{
		vec[i].id = create_writer_id();

		pthread_create(&vec[i].tid, nullptr, writer, (void*)vec[i].id.c_str());
	}
}

void join_thread(vector<ThreadAttr> const& vec)
{
	for (vector<ThreadAttr>::const_reverse_iterator it = vec.rbegin(); it != vec.rend(); it++)
	{
		pthread const& tid = it->tid;
		pthead_join(tid, nullptr);
	}
}

void init_rwlock()
{
	//写优先
	pthread_rwlockattr_t attr;
	pthread_rwlockattr_init(&attr);
	pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);//写者优先
	pthread_rwlock_init(&rwlock, &attr);
	pthread_rwlockattr_destroy(&attr);
	//读优先 pthread_rwlock_init(&rwlock, nullptr);
}


int main()
{
	const size_t reader_num = 1000;
	const size_t writer_num = 2;
	
	vector<ThreadAttr> reader(reader_num);
	vector<ThreadAttr> writer(writer_num);

	init_rwlock();

	init_readers(reader);
	init_readers(writer);

	join_threads(reader);
	join_threads(writer);

	pthread_rwlock_destroy(&rwlock);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章