一.实验目的:
通过实现读者写者问题理解进程及信号量的概念
二.实验要求:
创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。
三.实验内容:
利用多线程模拟读者与写者对临界区的互斥访问。读者和写者问题的读写操作限制:
可以有一个或多个数量的读进程同时读这个文件;一次只有一个写进程可以写文件;若一个写进程正在写文件,则禁止任何读进程读文件;即读读允许、读写互斥、写写互斥。
在读者写者问题中,使用读者优先和写者优两种解决方案。在读者优先策略中,读进程具有优先权,也就是说,当至少有一个读进程在读时,随后的读进程就无需等待,可以直接开始读操作,此过程中写进程可能会饥饿。在写者优先策略中,写进程具有优先权,写者优先与读者优先类似。不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个写者在等待,则新到来的读者不允许进行读操作,读者必须等到没有写者处于等待状态才能开始读操作。
四.算法描述:
读者优先:
为实现读者与写者进程间在读或写时的互斥而设置了一个互斥信号量filesrc,表示对资源的申请。当写者发出写请求时,必须先申请到资源,以此来实现读写互斥。另外,再设置一个整型变量readcount表示正在读的进程数目。由于只要有一个读进程Reader在读,便不允许写进程Writer去写。因此,仅当readcount=0,表示尚无Reader进程在读时,Reader进程才需要执行wait(filesrc)操作。若wait(filesrc)操作成功,Reader进程便可开始读,相应地,做readcount+1操作。同理,仅当Reader进程在执行了readcount减1操作后其值为0时,才须执行signal(filesrc)操作,以便让Writer进程进行写操作。又因为readcount是一个可被多个Reader进程访问的临界资源,因此,也应该为它设置一个互斥信号量rcsignal。
写者优先:
写者操作和读者操作相似,为实现读者与写者进程间在读或写时的互斥而设置了一个互斥信号量wrt,表示对资源的申请。读者写者等待进程数量通过记录当前堵塞的写者进程,与当前在读的读者进程数,所以引入readerCount与writerCount;写者操作要求有写者到来时应让写者先写。因此定义一个整型变量writercount,用来记录写者的数目,当writercount=0时才可以释放读者进行读操作。相应的,因为ReaderCount与WriterCount也是可以被多个读者、写者进程访问的临界资源,因此也需要设置互斥信号量RCSignaI与writeCountSignal。
互斥信号量read_s保证进程优先互斥,首先在读操作中申请读取资源,在执行读取操作代码之前释放该资源,同时在写操作中如果writercount 的值为 0,申请读取该资源,用于第一位写者申请资源,写操作完成后释放资源。该操作的作用是当写操作申请到该资源时,占用其直至写者堵塞排队队列都完成写操作,即会将所有读操作阻塞,以达到写者插队的效果。
五.运行结果与分析:
运行程序,首先提示输入1进行读者优先,输入2进行写者优先,输入3退出程序:
图1 程序运行结果图
首先输入1,开始进行读者优先操作。输入要处理的线程数,并分别输入线程序号、线程类别、线程延迟时间和线程读写操作时间:
图2 读者优先输入数据图
开始进行读者优先操作,首先由于线程1延迟时间最短,最先开始申请对文件进行操作,线程1申请读文件并可以开始读,该线程所需要的读取时间为5,即时间为8时线程1完成读操作;然后线程2在时间4开始申请写文件,由于此时线程1并没有释放资源,因此线程2不可以进行写操作,线程3在时刻5进入开始申请读文件,并开始读文件,此时线程5申请写文件,同样线程4在时刻6申请并开始读文件。线程3在时刻7完成读文件,线程1在时刻8完成读文件,线程4在时刻11完成读文件。此时所有读文件操作完成,开始进行写文件,线程2和5分别互斥进行写文件操作。至此五个线程全部完成操作。
程序结果图如下所示:
图3 读者优先运行结果图
完成读操作后进入首页,输入2,开始进行写者优先操作。输入要处理的线程数,并分别输入线程序号、线程类别、线程延迟时间和线程读写操作时间:
图4 写者优先输入数据图
开始进行写者优先操作,时刻3开始线程1申请读文件,同时开始读文件,时刻4线程2开始申请写文件,线程3在时刻5进入开始申请读文件,但是由于写者优先,所以线程3不能进行读文件操作,此时线程5申请写文件,同样线程4在时刻6申请并开始读文件。线程1在时刻8完成读文件,线程2和线程5可以互斥地进行写操作,写操作完成之后释放资源,线程3和线程4开始进行读操作,直至所有操作全部完成。
程序结果图如下所示:
图5 写者优先运行结果图
六.源程序:
#include<iostream>
#include<string>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <fstream>
#include <io.h>
#include <string.h>
#include<algorithm>
#include<Windows.h> //多线程编程
#include<process.h>
using namespace std;
#define READER 'R' //读者
#define WRITER 'W' //写者
#define INTE_PER_SEC 1000 //每秒时钟中断的数目
#define MAX_THREAD_NUM 64 //最大线程数目
//变量声明初始化
int readercount = 0;//记录等待的读者数目
int writercount = 0;//记录等待的写者数目
HANDLE rcsignal;//因为读者数量而添加的互斥信号量,用于读者优先
HANDLE RCSignal;//因为读者数量而添加的互斥信号量,用于写者优先
HANDLE writeCountSignal;//因为写者数量而添加的互斥信号量
HANDLE filesrc;//互斥访问信号量
HANDLE wrt;
//保证每次只有一个写者进行写操作,当写者的数量writercount等于0的时候,则证明此时没有没有读者了,释放信号量filesrc
HANDLE read_s;//进程优先互斥
struct thread_info {
int id; //线程序号
char type; //线程类别(判断是读者线程还是写者线程)
double delay; //线程延迟时间
double lastTime; //线程读写操作时间
};
//读者优先
//进程管理-读者线程
void rp_threadReader(void *p)
{
DWORD delaytime; //延迟时间
DWORD duration; //读文件持续时间
int num_thread; //线程序号
//从参数中获得信息
num_thread = ((thread_info*)(p))->id;
delaytime = (DWORD)(((thread_info*)(p))->delay *INTE_PER_SEC);
duration = (DWORD)(((thread_info*)(p))->lastTime *INTE_PER_SEC);
Sleep(delaytime); //延迟等待
cout << "读者线程" << num_thread << "申请读文件." << endl;
WaitForSingleObject(rcsignal, -1);
//申请互斥信号量rcsignal,多个读者对readercount互斥访问
if (readercount == 0)WaitForSingleObject(filesrc, -1);
//第一位读者申请书,仅当readercount为0时,表示此时无读者,reader进程才需要执行WaitForSingleObject
readercount++;
ReleaseSemaphore(rcsignal, 1, NULL);//释放互斥信号量rcsignal
cout << "读者线程" << num_thread << "开始读文件." << endl;
Sleep(duration);
cout << "读者线程" << num_thread << "完成读文件." << endl;
WaitForSingleObject(rcsignal, -1);//修改readercount
readercount--;//读者读完
if (readercount == 0)ReleaseSemaphore(filesrc, 1, NULL);//释放书籍,写者可写
ReleaseSemaphore(rcsignal, 1, NULL);//释放互斥信号量rcsignal
}
//读者优先
//进程管理-写者线程
void rp_threadWriter(void *p)
{
DWORD delaytime; //延迟时间
DWORD duration; //读文件持续时间
int num_thread; //线程序号
//从参数中获得信息
num_thread = ((thread_info*)(p))->id;
delaytime = (DWORD)(((thread_info*)(p))->delay *INTE_PER_SEC);
duration = (DWORD)(((thread_info*)(p))->lastTime *INTE_PER_SEC);
Sleep(delaytime); //延迟等待
cout << "写者线程" << num_thread << "申请写文件." << endl;
WaitForSingleObject(filesrc, INFINITE);//申请资源
cout << "写者线程" << num_thread << "开始写文件." << endl;
Sleep(duration);
cout << "写者线程" << num_thread << "完成写文件." << endl;
ReleaseSemaphore(filesrc, 1, NULL);//释放资源
}
//读者优先
void ReaderPriority()
{
DWORD n_thread = 0; //线程数目
DWORD thread_ID; //线程ID
DWORD wait_for_all; //等待所有线程结束
rcsignal = CreateSemaphore(NULL, 1, 1, (LPCWSTR)"mutex_for_readcount");// 创建信号量对象 读者对count修改互斥信号量,初值为1,最大为1
//(lpSemaphoreAttributes [in,optional]指向SECURITY_ATTRIBUTES结构的指针。如果此参数为NULL,则子进程无法继承句柄。、初值、最大值、信号量对象名称)
filesrc = CreateSemaphore(NULL, 1, 1, NULL);//书籍互斥访问信号量,初值为1,最大值为1
HANDLE h_Thread[MAX_THREAD_NUM];//线程句柄,线程对象的数组
thread_info thread_info[MAX_THREAD_NUM];//保存线程信息的数组
int id = 0;
readercount = 0; //初始化readcount
ifstream inFile; //打开文件
//inFile.open(file);
cout << "读者优先:" << endl;
cout << "请输入要处理的线程数:";
int i = 0,j;
cin >> j;
cout << "请分别输入线程序号、线程类别、线程延迟时间和线程读写操作时间"<<endl;
int id1;
char type1;
double delay1, lastTime1;
while (i < j) {
cin >> id1;
cin >> type1;
cin >> delay1;
cin >> lastTime1;
thread_info[n_thread].id = id1;
thread_info[n_thread].type = type1;
thread_info[n_thread].delay = delay1;
thread_info[n_thread++].lastTime = lastTime1;
i++;
}
for (int i = 0; i < (int)(n_thread); i++)
{
if (thread_info[i].type == READER || thread_info[i].type == 'R') {//线程类别是读者 //安全设置、堆栈大小、入口函数指针、函数参数、启动选项、输出线程 ID ,返回线程句柄
//创建读者进程
h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(rp_threadReader), &thread_info[i], 0, &thread_ID);
}
else {
//创建写线程
h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(rp_threadWriter), &thread_info[i], 0, &thread_ID);
}
}
//等待子线程结束
//关闭句柄
wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1); //等待所有线程结束
cout << endl;
cout << "所有读者写者已经完成操作!" << endl;
for (int i = 0; i < (int)(n_thread); i++)
CloseHandle(h_Thread[i]);
CloseHandle(rcsignal);
CloseHandle(filesrc);
}
//写者优先
//进程管理-读者线程
void wp_threadReader(void *p) {
DWORD delaytime; //延迟时间
DWORD duration; //读文件持续时间
int num_thread; //线程序号
//从参数中获得信息
num_thread = ((thread_info*)(p))->id;
delaytime = (DWORD)(((thread_info*)(p))->delay *INTE_PER_SEC);
duration = (DWORD)(((thread_info*)(p))->lastTime *INTE_PER_SEC);
Sleep(delaytime); //延迟等待
//printf("读者进程%d申请读文件.\n", num_thread);
cout << "读者线程" << num_thread << "申请读文件." << endl;
WaitForSingleObject(read_s, -1); //申请读取权限
WaitForSingleObject(RCSignal, -1);//对readercount互斥访问,即多个读者互斥
if (readercount == 0)
WaitForSingleObject(wrt, -1);
//第一位读者申请书,同时防止写者进行写操作
readercount++;
ReleaseSemaphore(RCSignal, 1, NULL);//释放互斥信号量rcsignal
ReleaseSemaphore(read_s, 1, NULL);//释放互斥信号量read
/* reading is performed */
cout << "读者线程" << num_thread << "开始读文件." << endl;
Sleep(duration);
cout << "读者线程" << num_thread << "完成读文件." << endl;
WaitForSingleObject(RCSignal, -1);//修改readercount
readercount--;//读者读完
if (readercount == 0)
ReleaseSemaphore(wrt, 1, NULL);//释放资源,写者可写
ReleaseSemaphore(RCSignal, 1, NULL);//释放互斥信号量rcsignal
}
//写者优先
//进程管理-写者线程
void wp_threadWriter(void *p) {
DWORD delaytime; //延迟时间
DWORD duration; //读文件持续时间
int num_thread; //线程序号
//从参数中获得信息
num_thread = ((thread_info*)(p))->id;
delaytime = (DWORD)(((thread_info*)(p))->delay *INTE_PER_SEC);
duration = (DWORD)(((thread_info*)(p))->lastTime *INTE_PER_SEC);
Sleep(delaytime); //延迟等待
cout << "写者线程" << num_thread << "申请写文件." << endl;
WaitForSingleObject(writeCountSignal, -1);//对writercount互斥访问,申请写者计数器资源
if (writercount == 0)
WaitForSingleObject(read_s, -1);
//第一位写者申请资源
writercount++;
ReleaseSemaphore(writeCountSignal, 1, NULL);//释放写者计数器资源
WaitForSingleObject(wrt, -1);//申请文件资源
/*write is performed*/
cout << "写者线程" << num_thread << "开始写文件." << endl;
Sleep(duration);
cout << "写者线程" << num_thread << "完成写文件." << endl;
ReleaseSemaphore(wrt, 1, NULL);//释放资源
WaitForSingleObject(writeCountSignal, -1);//对writercount互斥访问,即多个写者互斥
writercount--;
if (writercount == 0)
ReleaseSemaphore(read_s, 1, NULL);
//释放资源
ReleaseSemaphore(writeCountSignal, 1, NULL);//释放资源
}
//写者优先
void WriterPriority() {
DWORD n_thread = 0; //线程数目
DWORD thread_ID; //线程ID
DWORD wait_for_all; //等待所有线程结束
//创建信号量
RCSignal = CreateSemaphore(NULL, 1, 1, (LPCWSTR)"mutex_for_readercount");//读者对count修改互斥信号量,初值为1,最大为1
writeCountSignal = CreateSemaphore(NULL, 1, 1, (LPCWSTR)"mutex_for_writercount");//写者对count修改互斥信号量,初值为1,最大为1
wrt = CreateSemaphore(NULL, 1, 1, NULL);//
read_s = CreateSemaphore(NULL, 1, 1, NULL);//书籍互斥访问信号量,初值为1,最大值为1
HANDLE h_Thread[MAX_THREAD_NUM];//线程句柄,线程对象的数组
thread_info thread_info[MAX_THREAD_NUM];//保存线程信息的数组
int id = 0;
readercount = 0; //初始化readcount
writercount = 0;
ifstream inFile;
//inFile.open(file);
cout << "写者优先:" << endl;
cout << "请输入要处理的线程数:";
int i = 0, j;
cin >> j;
cout << "请分别输入线程序号、线程类别、线程延迟时间和线程读写操作时间" << endl;
int id1;
char type1;
double delay1, lastTime1;
while (i < j) {
cin >> id1;
cin >> type1;
cin >> delay1;
cin >> lastTime1;
thread_info[n_thread].id = id1;
thread_info[n_thread].type = type1;
thread_info[n_thread].delay = delay1;
thread_info[n_thread++].lastTime = lastTime1;
i++;
}
for (int i = 0; i < (int)(n_thread); i++)
{
if (thread_info[i].type == READER || thread_info[i].type == 'r')
{
//创建读者进程
h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(wp_threadReader), &thread_info[i], 0, &thread_ID);
}
else
{
//创建写线程
h_Thread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(wp_threadWriter), &thread_info[i], 0, &thread_ID);
}
}
//等待子线程结束
//关闭句柄
wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1);
cout << endl;
cout << "所有读者写者已经完成操作!!" << endl;
for (int i = 0; i < (int)(n_thread); i++)
CloseHandle(h_Thread[i]);
CloseHandle(writeCountSignal);
CloseHandle(RCSignal);
CloseHandle(wrt);
}
//主函数
int main()
{
char choice;
//cout << " 读写操作测试程序 " << endl;
cout << endl;
while (true)
{
//打印提示信息
cout << "请按照需求输入序号: " << endl;
cout << "1、读者优先" << endl;
cout << "2、写者优先" << endl;
cout << "3、退出程序" << endl;
cout << endl;
//如果输入信息不正确,继续输入
do {
choice = (char)_getch();
} while (choice != '1'&&choice != '2'&&choice != '3');
system("cls");//清屏
//选择1,读者优先
if (choice == '1') {
//ReaderPriority("thread.txt");
ReaderPriority();
system("pause");
}
//选择2,写者优先
else if (choice == '2') {
WriterPriority();
system("pause");
}
//选择3,退出
else
return 0;
//结束
cout<<"\nPress Any Key to Coutinue";
_getch();
system("cls");
}
return 0;
}