[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);
}



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