操作系统原理实验-进程同步

操作系统原理实验报告

 实验题目   实二进程同步   

实验二、进程同步

1.1 实验目的

  1. 现代操作系统的核心是多道程序设计、多处理器和分布式处理器,这些方案和操作系统设计技术的基础都是并发。当多个进程并发执行时,不论是在多处理器系统的情况下,还是在单处理器多道程序系统中,都会出现冲突和合作的问题。
  2. 理解操作系统中用互斥和同步解决问题。
  3. 用信号量机制分别实现读者优先和写者优先的读者-写者问题从而掌握互斥与同步的基本理念。

1.2 实验内容及要求

  1. 在Windows 环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件(后面有介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。
  2. 读者-写者问题的读写操作限制(包括读者优先和写者优先):
  3. 1) 写-写互斥,即不能有两个写者同时进行写操作。
  4. 2) 读-写互斥,即不能同时有一个线程在读,而另一个线程在写。
  5. 3) 读-读允许,即可以有一个或多个读者在读。
  6. 读者优先的附加限制:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。
  7. 写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态才能开始读操作。
  8. 运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结果读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。
  9. 测试数据文件包括n行测试数据,分别描述创建的n个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,各个字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二字段表示相应线程角色,R表示读者,W表示写者。第三字段为一个正数,表示读写操作的开始时间:线程创建后,延迟相应时间(单位为秒)后发出对共享资源的读写申请。第四字段为一个正数,表示读写操作的持续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。

下面是一个测试数据文件的例子:

1       R       3       5

                                                               2       W       4       5

3       R       5       2

4       R       6       5

                                                               5       W       5.1     3

1.3 实验技术

  1. 在开始本实验之前,请回顾教科书的相关内容
  2. 需要做以下准备:

1.一台运行Windows 操作系统的计算机

2.计算机中需安装Visual C专业版或企业版

1.4实验步骤

(1)在Windows 环境下,创建一个控制台进程,此进程包含n个线程。用

这n个线程来表示n个读者或写者。每个线程按相应测试数据文件(后面有

介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的

(2)读者-写者问题。读者-写者问题的读写操作限制(包括读者优先和写者优先):

(3)使用测试数据文件运行,分析实验结果

实验代码流程图如下:

1.5实验结果及分析说明

编译示例代码,跳出选择界面,如下图图所示:

下面是一个测试数据文件的例子:

1      R        3       5

2      W        4       5

3      R        5       2

4      R        6       5

5      W        5.1     3

将该内容写至txt文档中,修改示例代码的file为该txt文本的位置。

选择读文件优先,即输入1,运行结果如下所示:

流程:

①进程1发送读请求,并进入读文件。

②进程2发送写请求,读写互斥,所以不能进行写操作。

③进程3发送读请求,并进入读文件。

④进程5发送写请求,读写互斥,所以不能进行写操作。

⑤进程4发送读请求,并进入读文件。

⑥进程3,1,4先后完成读操作。

⑦进程2开始写操作,结束。

⑧进程5开始写操作,结束

选择写优先优先,即输入2,运行结果如下所示:

流程:

①进程1发送写请求,并进入写文件。

②进程2发送写请求,并进入写文件。

③进程3,5,4发送写请求。

④进程1结束,进程3写操作开始。

⑤进程2结束,进程5写操作开始。

⑥进程3结束,进程4写操作开始。

⑦进程5,4结束。

实验结果的分析:

将所有读者和所有写者分别存于一个读者等待队列和一个写者等待队列中,每当读允许时,就从读者队列中释放一个或多个读者线程进行读操作;每当写允许时,就从写者队列中释放一个写者进行写操作。

读者优先的情况:

    读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整型变

read-count 记录当前的读者数目,用于确定是否需要释放正在等待的写者线程(当

read-count=0 时,表明所有的读者读完,需要释放写者等待队列中的一个写者)。每一个读

者开始读文件时,必须修改 read-count 变量。因此需要一个互斥对象 mutex 来实现对全局变量 read-count 修改时的互斥。

另外,为了实现写-写互斥,需要增加一个临界区对象 write。当写者发出写请求时,必须申请临界区对象的所有权。通过这种方法,也可以实现读-写互斥,当 read-count=1

(即第一个读者到来时),读者线程也必须申请临界区对象的所有权。当读者拥有临界区的所有权时,写者阻塞在临界区对象 write 上。当写者拥有临界区的所有权时,第一个读者判断完“read-count==1”后阻塞在 write 上,其余的读者由于等待对 read-count 的判断,阻塞在 mutex 上。

写者优先的情况:

写者优先与读者优先类似。不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个写者在等待,则新到来的读者不允许进行读操作。为此应当添加一个整

型变量 write-count,用于记录正在等待的写者数目,当 write-count=0 时,才可以释放等待的读者线程队列。为了对全局变量 write-count 实现互斥,必须增加一个互斥对象 mutex3

为了实现写者优先,应当添加一个临界区对象 read,当有写者在写文件或等待时,读

者必须阻塞在 read 上。读者线程除了要对全局变量 read-count 实现操作上的互斥外,还必须有一个互斥对象 对阻塞 read 这一过程实现互斥。这两个互斥对象分别命名为 mutex1 mutex2

  

1.6实验体会(实验中遇到的问题及解决方法

1)实验反思

示例代码很长,阅读起来很困难,代码逻辑严谨,进一步体会到了进程同步和互斥。

2)实验收获

1.可以将所有读者和所有写者分别存于一个读者等待队列和一个写者等待队列中;每当读允许时,就从读者队列中释放一个或多个读者线程进行读操作;每当写允许时,就从写者队列中释放一个写者进行写操作。

2.了解了进程同步涉及的一些API的作用:

①CreateThread功能:创建一个线程。

②CreateMutex功能:产生一个命名的互斥量对象。

③CreateSemaphore功能:创建命名或匿名的互斥量对象。

④WaitForSingleObject功能:使程序处于等待状态,直到信号量出现或超过规定等待的最长时间,信号量大于或等于1。

⑤ReleaseSemaphore功能:将所指信号量加上指定大小的一个量,执行成功返回非0值。

⑥ReleaseMutex功能:用来打开互斥锁,即将互斥量加1,成功调用则返回0。

⑦InitializeCriticalSection功能:初始化临界区对象。

⑧EnterCriticalSection功能:等待指定临界区对象的所有权。

⑨LeaceCriticalSetion功能:释放指定临界区对象指针。

源码附录:




#include "windows.h"
#include <conio.h>
#include <stdlib.h>
#include <fstream.h>
#include <io.h>
#include <string.h>
#include <stdio.h>
#define READER 'R' // 读者
#define WRITER 'W' // 写者
#define INTE_PER_SEC 1000 // 每秒时钟中断数目。
#define MAX_THREAD_NUM 64 // 最大线程数目
#define MAX_FILE_NUM 32 // 最大数据文件数目
#define MAX_STR_LEN 32 // 字符串长度
int readcount=0; // 读者数目
int writecount=0; // 写者数目
CRITICAL_SECTION RP_Write; //临界区
CRITICAL_SECTION cs_Write;
CRITICAL_SECTION cs_Read;
struct ThreadInfo
{
int serial; // 线程序号
char entity; //线程类别(判断读者线程还是写者线程)
double delay;
double persist;
};
///
// 读者优先--读者线程
//p:读者线程信息
void RP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_Mutex;
h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_readcount");
DWORD wait_for_mutex; //等待互斥变量所有权
DWORD m_delay; // 延迟时间
DWORD m_persist; // 读文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Reader thread %d sents the reading require.\n",m_serial);
// 等待互斥信号,保证对 readcount 的访问、修改互斥
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
//读者数目增加
readcount++;
if(readcount==1)
{
 //第一个读者,等待资源
 EnterCriticalSection(&RP_Write);
}
ReleaseMutex(h_Mutex); //释放互斥信号
//读文件
printf("Reader thread %d begins to read file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Reader thread %d finished reading file.\n",m_serial);
//等待互斥信号,保证对 readcount 的访问、修改互斥
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
//读者数目减少
readcount--;
if(readcount==0)
{
 //如果所有读者读完,唤醒写者
 LeaveCriticalSection(&RP_Write);
}
ReleaseMutex(h_Mutex); //释放互斥信号
}



///
// 读者优先--写者线程
//p:写者线程信息
void RP_WriterThread(void* p)
{
DWORD m_delay; // 延迟时间
DWORD m_persist; // 写文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p)) ->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Writer thread %d sents the writing require.\n",m_serial);
// 等待资源
EnterCriticalSection(&RP_Write);
//写文件
printf("Writer thread %d begins to Write to the file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Writer thread %d finished Writing to the file.\n",m_serial);
//释放资源
LeaveCriticalSection(&RP_Write);
}



///
// 读者优先处理函数
//file:文件名
void ReaderPriority(char* file)
{
DWORD n_thread=0; //线程数目
DWORD thread_ID; //线程 ID
DWORD wait_for_all; //等待所有线程结束
//互斥对象
HANDLE h_Mutex;
h_Mutex=CreateMutex(NULL,FALSE,"mutex_for_readcount");
//线程对象的数组
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0; // 初始化 readcount
InitializeCriticalSection(&RP_Write); //初始化临界区
ifstream inFile;
inFile.open("D:\\testdata.txt"); //打开文件
printf("Reader Priority:\n\n");
while(inFile)
{
 //读入每一个读者、写者的信息
 inFile>>thread_info[n_thread].serial;
 inFile>>thread_info[n_thread].entity;
 inFile>>thread_info[n_thread].delay;
 inFile>>thread_info[n_thread++].persist;
 inFile.get( );
}
for(int i=0;i< (int)(n_thread);i++)
{
 if(thread_info[i].entity==READER || thread_info[i].entity=='R')
 {
 
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_ReaderThread),&thread_info[i],0,&thread_ID); //创建读者线程
 }
else{
 //创建写者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
 } 
}
//等待所有线程结束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);
printf("All reader and writer have finished operating.\n");
}
///
// 写者优先--读者线程
//p:读者线程信息
void WP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_Mutex1;
h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex1");
HANDLE h_Mutex2;
h_Mutex2=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex2");
DWORD wait_for_mutex1; //等待互斥变量所有权
DWORD wait_for_mutex2;
DWORD m_delay; // 延迟时间
DWORD m_persist; // 读文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p)) ->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Reader thread %d sents the reading require.\n",m_serial);
wait_for_mutex1= WaitForSingleObject(h_Mutex1,-1);
//进入读者临界区
 EnterCriticalSection(&cs_Read);
// 阻塞互斥对象 mutex2,保证对 readcount 的访问、修改互斥
wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);
//修改读者数目
readcount++;
if(readcount==1)
{
 //如果是第一个读者,等待写者写完
 EnterCriticalSection(&cs_Write);
}
ReleaseMutex(h_Mutex2); //释放互斥信号 mutex2
// 让其他读者进入临界区
LeaveCriticalSection(&cs_Write);
ReleaseMutex(h_Mutex1);
//读文件
printf("Reader thread %d begins to read file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Reader thread %d finished reading file.\n",m_serial);
// 阻塞互斥对象 mutex2,保证对 readcount 的访问、修改互斥
wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);
readcount--;
if(readcount==0)
{
 // 最后一个读者,唤醒写者
 LeaveCriticalSection(&cs_Write);
}
ReleaseMutex(h_Mutex2); //释放互斥信号
}
///
// 写者优先--写者线程
//p:写者线程信息
void WP_WriterThread(void* p)
{
DWORD m_delay; // 延迟时间
DWORD m_persist; // 写文件持续时间
int m_serial; //线程序号
DWORD wait_for_mutex3;
//互斥对象
HANDLE h_Mutex3;
h_Mutex3= OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex3");
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Writer thread %d sents the writing require.\n",m_serial);
// 阻塞互斥对象 mutex3,保证对 writecount 的访问、修改互斥
wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);
writecount++; //修改读者数目
if(writecount==1)
{
 //第一个写者,等待读者读完
 EnterCriticalSection(&cs_Read);
}
ReleaseMutex(h_Mutex3);
//进入写者临界区
EnterCriticalSection(&cs_Write);
//写文件
printf("Writer thread %d begins to Write to the file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Writer thread %d finishing Writing to the file.\n",m_serial);
//离开临界区
LeaveCriticalSection(&cs_Write);
// 阻塞互斥对象 mutex3,保证对 writecount 的访问、修改互斥
wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);
writecount--; //修改读者数目
if(writecount==0)
{
 //写者写完,读者可以读
 LeaveCriticalSection(&cs_Read);
}
ReleaseMutex(h_Mutex3);
}
///
// 写者优先处理函数
//file:文件名
void WriterPriority(char* file)
{
DWORD n_thread=0; //线程数目
DWORD thread_ID; //线程 ID
DWORD wait_for_all; //等待所有线程结束
//互斥对象
HANDLE h_Mutex1;
h_Mutex1=CreateMutex(NULL,FALSE,"mutex1");
HANDLE h_Mutex2;
h_Mutex2=CreateMutex(NULL,FALSE,"mutex2");
HANDLE h_Mutex3;
h_Mutex3=CreateMutex(NULL,FALSE,"mutex3");
//线程对象
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0; // 初始化 readcount
writecount=0; // 初始化 writecount
InitializeCriticalSection(&cs_Write); //初始化临界区
InitializeCriticalSection(&cs_Read);
ifstream inFile;
inFile.open("D:\\testdata.txt"); //打开文件
printf("Writer Priority:\n\n");
while(inFile)
{
 //读入每一个读者、写者的信息
 inFile>>thread_info[n_thread].serial;
 inFile>>thread_info[n_thread].entity;
 inFile>>thread_info[n_thread].delay;
 inFile>>thread_info[n_thread++].persist;
 inFile.get( );
}
for(int i=0;i< (int)(n_thread);i++)
{
 if (thread_info[i].entity==READER || thread_info[i].entity=='R')
 {
 //创建读者线程
 
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
 }
else{
 //创建写者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_WriterThread),&thread_info[i],0,&thread_ID);
 }
}
//等待所有线程结束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);
printf("All reader and writer have finished operating.\n");
}
///
//主函数
int main(int argc,char* argv[])
{
char ch;
while (true)
{
 //打印提示信息
 printf("************************************************\n");
 printf(" 1:Reader Priority\n");
 printf(" 2:Writer Priority\n");
 printf(" 3:Exit Priority\n");
 printf("************************************************\n");
 printf("Enter your choice(1,2 or 3): ");
 //如果输入信息不正确,继续输入
 do{
 ch=(char)_getch( );
 }while(ch != '1' &&ch != '2' && ch != '3');
 system("cls");
 //选择 3,返回
 if(ch=='3')
 return 0;
 //选择 1,读者优先
 else if(ch=='1')
 ReaderPriority("thread.dat");
 //选择 2,写者优先
 else if(ch=='2')
 WriterPriority("thread.dat");
 //结束
 printf("\nPress Any Key To Continue: ");
 _getch( );
 system("cls");
}
return 0;
}

猜你喜欢

转载自blog.csdn.net/cangzhexingxing/article/details/124856879