欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux操作系统--读者写者问题(读者优先)

程序员文章站 2022-07-04 23:44:14
...

为什么要进行并发控制

在并发程序中,多个进程或者线程的运行,本来是可以毫无关系的,也就是完全的异步执行,但是现实中往往异步执行不能满足需求

  • 假如只有一个苹果,A和B都要执行吃苹果的程序,如果只是异步执行的话,他们都会认为自己吃完了苹果,但是无法确定到底谁吃到了苹果

引出的问题就是并发执行的程序要想对共同的资源进行访问,需要实现同步,才可以保证对于共同资源的有序使用,避免出现问题。

P/V信号量编程

  • 临界资源:并发程序需要访问的相同的资源
  • P操作是指,检测临界区域能否使用,等待资源
  • V操作是指,释放临界资源,通知等待的进程

实现读者写者的并发控制(读者优先)

  • 问题分析
    对于同样的资源,读的时候不能写,写的时候不能读,也就是读写互斥,用信号量可以完美实现

  • 实现思路

    • 读者线程
      • 读者可以并发的读临界资源,因此,可以不对读操作加互斥量
      • 读者优先的特性应该在读者线程中体现出来,即只有读者全部读完之后,才会释放临界资源
      • 为了保证所有的读者都读完再进行临界资源的释放,需要统计读者的数量,每个读线程为readCount加1,读完之后再减1
      • 关键对于readCount加1减1的操作,如果不进行同步,多个读线程共同操作,会出现同步问题,因此,需要对readCount的操作加互斥量,保证只有一个读者对这一数字进行改变
    • 写者线程
      写者只能单独写临界资源,因此,需要对写操作加互斥量
  • 待解决疑问
    如果一个写线程在使用资源,一个读线程和一个写线程都在等待资源,如何保证写线程写完释放资源之后,可以将资源分配给读线程,保证读者优先

  • 代码分析

#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <fstream>
#include <iostream>
using namespace std;

#define sleep(n) Sleep(n*1000)
struct ThreadInfo
{
	int tid;            //线程ID
	char role;         //扮演角色 R or W
	double delay;      //线程延迟
	double persist;    //线程读写操作持续时间
};
int ReadCount = 0;     //读者数量
HANDLE Rmutex;    //信号量
CRITICAL_SECTION RW_mutex; //临界区
void ReaderThread(void * p)
{
	//创建线程会传入相关参数
	//DWORD全称Double Word,每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。
	DWORD m_delay = ((ThreadInfo *)(p))->delay;
	DWORD m_persist = ((ThreadInfo *)(p))->persist;
	int m_id = ((ThreadInfo*)(p))->tid;
	
	//模拟读者想要读之前的等待时间
	sleep(m_delay); 
	printf("Reader thread %d sents the reading require ! \n", m_id);
	
	//获取操作readCount的权限,对readCount加1
	DWORD wait_for_mutex = WaitForSingleObject(Rmutex, -1); 
	//获取临界资源
	if (ReadCount == 0)
	{
		 //等待临界区,直到进入临界区
		 EnterCriticalSection(&RW_mutex); 
		 //DWORD waits = WaitForSingleObject(RW_mutex, -1);
	}
	//统计有几个读者
	ReadCount++;
	//通知其他读者
	ReleaseMutex(Rmutex);
	
	// 模拟读者进行阅读
	printf("Reader thread %d begins to read file ! \n", m_id);
	sleep(m_persist);
	printf("Reader thread %d finished reading file ! \n", m_id);
	
	wait_for_mutex = WaitForSingleObject(Rmutex, -1);
	ReadCount--;
	//没有读者才释放临界区,也是为什么前边需要统计读者数量的原因,
	//因为要让所有的读者都读完,才释放临界区
	if (ReadCount == 0)
	{
		//释放临界区
		LeaveCriticalSection(&RW_mutex);  						//ReleaseMutex(RW_mutex);
	}
	//释放互斥信号量,通知读者来读
	ReleaseMutex(Rmutex);
}

void WriterThread(void * p)
{
	DWORD m_delay;
	DWORD m_persist;
	int m_id;
	//从参数中获取信息
	m_delay = ((ThreadInfo *)(p))->delay;
	m_persist = ((ThreadInfo *)(p))->persist;
	m_id = ((ThreadInfo*)(p))->tid;
	sleep(m_delay); //延迟等待
	printf("Writer thread %d sents the writing require ! \n", m_id);
	//等待进入临界区,直到进入,P操作
	EnterCriticalSection(&RW_mutex); //DWORD waits = WaitForSingleObject(RW_mutex, -1);
	//执行写操作
	printf("Writer thread %d begins to write file ! \n", m_id);
	sleep(m_persist);
	printf("Writer thread %d finished writing file ! \n", m_id);
	//释放资源,V操作
	LeaveCriticalSection(&RW_mutex);//ReleaseMutex(RW_mutex);
}

int main(int argc, char ** argv)
{
	DWORD n_thread = 0;  //线程数目
	DWORD thread_ID;     //线程ID
	//线程对象数组
	HANDLE h_thread[20];
	ThreadInfo thread_info[20];
	//初始化同步对象
	Rmutex = CreateMutex(NULL, FALSE, LPCWSTR("mutex_for_readcount"));
	InitializeCriticalSection(&RW_mutex); //初始化临界区                  //RW_mutex = OpenMutex(MUTEX_ALL_ACCESS, false, LPCWSTR("mutex_for"));
	//读取输入文件
	ifstream inFile;
	inFile.open("rw.txt");
	if (!inFile)
	{
		printf("error in open file !\n");
		return -1;
	}
	while (inFile)
	{
		inFile >> thread_info[n_thread].tid;
		inFile >> thread_info[n_thread].role;
		inFile >> thread_info[n_thread].delay;
		inFile >> thread_info[n_thread].persist;
		inFile.get();
		n_thread++;
	}
	//创建线程
	for (int i = 0; i < n_thread; i++)
	{
		if (thread_info[i].role == 'R' || thread_info[i].role == 'r') 
		{
		//根据上述方法创建读者线程
			h_thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(ReaderThread), &thread_info[i], 0, &thread_ID);
		}
		else
		{
		//根据上述方法创建写者线程
			h_thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(WriterThread), &thread_info[i], 0, &thread_ID);
		}
	}
	//等待所有线程结束,直到结束
	DWORD wait_for_all = WaitForMultipleObjects(n_thread, h_thread, true, -1);
	printf("All reader and writer have finished operating !\n");
	_getch();
	return 0;
}