【实验目的】

理解临界区和进程互斥的概念,掌握用信号量和PV操作实现进程互斥的方法。

【实验内容】

在windows或者linux环境下编写一个控制台应用程序,该程序运行时能创建N个线程,其中既有读者线程又有写者线程,它们按照事先设计好的测试数据进行读写操作。请用信号量和PV操作实现读者/写者问题。

读者/写者问题的描述如下:

有一个被许多进程共享的数据区,这个数据区可以是一个文件,或者主存的一块空间,甚至可以是一组处理器寄存器。有一些只读取这个数据区的进程(reader)和一些只往数据区中写数据的进程(writer)。以下假设共享数据区是文件。这些读者和写者对数据区的操作必须满足以下条件:读—读允许;读—写互斥;写—写互斥。这些条件具体来说就是:

(1)任意多的读进程可以同时读这个文件;

(2)一次只允许一个写进程往文件中写;

(3)如果一个写进程正在往文件中写,禁止任何读进程或写进程访问文件;

(4)写进程执行写操作前,应让已有的写者或读者全部退出。这说明当有读者在读文件时不允许写者写文件。

对于读者-写者问题,有三种解决方法:

1、读者优先

除了上述四个规则外,还增加读者优先的规定,当有读者在读文件时,对随后到达的读者和写者,要首先满足读者,阻塞写者。这说明只要有一个读者活跃,那么随后而来的读者都将被允许访问文件,从而导致写者长时间等待,甚至有可能出现写者被饿死的情况。

2、写者优先

除了上述四个规则外,还增加写者优先的规定,即当有读者和写者同时等待时,首先满足写者。当一个写者声明想写文件时,不允许新的读者再访问文件。

3、无优先

除了上述四个规则外,不再规定读写的优先权,谁先等待谁就先使用文件。

【实验原理】

【程序代码】

#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <string.h>
#define MAX_THREAD 10           //待测试的线程数
typedef struct{                      //表示测试数据格式char thread_name[3];            //线程名unsigned int require_moment;    //请求操作时刻unsigned int persist_time;        //操作持续时间
}TEST_INFO;
TEST_INFO test_data[MAX_THREAD]={    //测试数据表{"r1",0,15},                          // r表示读者线程{"r2",1, 15},                         //w表示写者线程{"w1",3,3},{"r3",4, 2},{"w2",5,6},{"w3",6,10},{"r4",7,8},{"r5",9,2},{"w4",10,18},{"w5",12,2}
};int read_count=0;                     //记录正在读文件的读者数
int write_count=0;                   //在写者优先算法中记录声明要写文件的写者数
CRITICAL_SECTION CS_DATA;       //用于保护文件的临界区变量
HANDLE h_mutex_read_count=CreateMutex(NULL,FALSE,"mutex_read_count");
//读者计数器互斥体
HANDLE h_mutex_write_count=CreateMutex(NULL,FALSE,"mutex_write_count");
//写者计数器互斥体
HANDLE h_mutex_reader_wait=CreateMutex(NULL,FALSE,"mutex_reader_wait");
//在写者优先算法中用于阻塞读者的互斥体
HANDLE h_mutex_first_reader_wait=
CreateMutex(NULL,FALSE,"mutex_first_reader_wait");
//在写者优先算法中用于阻塞第一个读者的互斥体
HANDLE h_mutex_wait=CreateMutex(NULL,FALSE,"mutex_wait");
//无优先时用于阻塞读者和写者的互斥体//读者优先时的读者线程
void RF_reader_thread(void *data){char thread_name[3];                        //存放线程名称strcpy(thread_name,((TEST_INFO *)data)->thread_name);Sleep(((TEST_INFO *)data)->require_moment*1000);WaitForSingleObject(h_mutex_read_count,-1);
//申请进入关于读者计数器的临界区相当于P操作read_count++;if(read_count==1)EnterCriticalSection(&CS_DATA); //申请进入关于文件的临界区相当于P操作ReleaseMutex(h_mutex_read_count);
//离开关于读者计数器的临界区相当于V操作printf("%s ",thread_name);Sleep(((TEST_INFO *)data)->persist_time*1000); //用延迟相应秒来模拟读文件操作WaitForSingleObject(h_mutex_read_count,-1);read_count--;if(read_count==0)LeaveCriticalSection(&CS_DATA);//离开关于文件的临界区相当于V操作ReleaseMutex(h_mutex_read_count);
}//读者优先时的写者线程
void RF_writer_thread(void *data){Sleep(((TEST_INFO *)data)->require_moment*1000);//用延迟相应秒来模拟写文件操作EnterCriticalSection(&CS_DATA);//申请进入,相当于P操作 printf("%s ",((TEST_INFO *)data)->thread_name);Sleep(((TEST_INFO *)data)->persist_time*1000); //用延迟相应秒来模拟写文件操作LeaveCriticalSection(&CS_DATA);//离开,相当于V操作
}//读者优先时的初始化程序
void reader_first(){int i=0;HANDLE h_thread[MAX_THREAD];printf("读优先申请次序:");for(i=0;i<MAX_THREAD;i++){printf("%s ",test_data[i].thread_name);};printf("\n");printf("读优先操作次序:");InitializeCriticalSection(&CS_DATA);            //初始化临界区变量for(i=0;i<MAX_THREAD;i++){           //根据线程名称创建不同的线程if(test_data[i].thread_name[0]=='r')  //名称的首字母是'r'则创建读者线程h_thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RF_reader_thread),&test_data[i],0,NULL);else                           //名称的首字母是'w'则创建写者线程        h_thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RF_writer_thread),&test_data[i],0,NULL);}WaitForMultipleObjects(MAX_THREAD,h_thread,TRUE,-1); //等待所有线程结束printf("\n");
}
//无优先时的读者线程
void FIFO_reader_thread(void *data){char thread_name[3];strcpy(thread_name,((TEST_INFO *)data)->thread_name);Sleep(((TEST_INFO *)data)->require_moment*1000);//用延迟相应秒来模拟写文件操作WaitForSingleObject(h_mutex_wait,-1);//申请进入 WaitForSingleObject(h_mutex_read_count,-1);//申请进入关于读者计数器的临界区相当于P操作read_count++;if(read_count==1)EnterCriticalSection(&CS_DATA);ReleaseMutex(h_mutex_read_count);//离开关于读者计数器的临界区相当于V操作ReleaseMutex(h_mutex_wait);//离开关于等待计数器的临界区相当于V操作printf("%s ",thread_name);Sleep(((TEST_INFO *)data)->persist_time*1000);//用延迟相应秒来模拟写文件操作WaitForSingleObject(h_mutex_read_count,-1);read_count--;if(read_count==0)LeaveCriticalSection(&CS_DATA);ReleaseMutex(h_mutex_read_count);//离开关于读者计数器的临界区相当于V操作
}//无优先时的写者线程
void FIFO_writer_thread(void *data){Sleep(((TEST_INFO *)data)->require_moment*1000);//用延迟相应秒来模拟写文件操作WaitForSingleObject(h_mutex_wait,-1);EnterCriticalSection(&CS_DATA);printf("%s ",((TEST_INFO *)data)->thread_name);Sleep(((TEST_INFO *)data)->persist_time*1000);//用延迟相应秒来模拟写文件操作LeaveCriticalSection(&CS_DATA);ReleaseMutex(h_mutex_wait);
}//无优先时的初始化程序
void first_come_first_served(){int i=0;HANDLE h_thread[MAX_THREAD];printf("无优先申请次序:");for(i=0;i<MAX_THREAD;i++){printf("%s ",test_data[i].thread_name);};printf("\n");printf("无优先操作次序:");InitializeCriticalSection(&CS_DATA);//初始化临界区变量for(i=0;i<MAX_THREAD;i++){//根据线程名称创建不同的线程if(test_data[i].thread_name[0]=='r')//名称的首字母是'r'则创建读者线程h_thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(FIFO_reader_thread),&test_data[i],0,NULL);else//名称的首字母是'w'则创建写者线程h_thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(FIFO_writer_thread),&test_data[i],0,NULL);}WaitForMultipleObjects(MAX_THREAD,h_thread,TRUE,-1); //等待所有线程结束printf("\n");
}//写者优先时的读者线程
void WF_reader_thread(void *data){char thread_name[3];strcpy(thread_name,((TEST_INFO *)data)->thread_name);Sleep(((TEST_INFO *)data)->require_moment*1000);//用延迟相应秒来模拟写文件操作WaitForSingleObject(h_mutex_reader_wait,-1);//申请进入,相当于P操作WaitForSingleObject(h_mutex_first_reader_wait,-1);//申请进入,相当于P操作WaitForSingleObject(h_mutex_read_count,-1);//申请进入,相当于P操作read_count++;if(read_count==1)EnterCriticalSection(&CS_DATA);ReleaseMutex(h_mutex_read_count);//离开,相当于V操作 ReleaseMutex(h_mutex_first_reader_wait);//离开,相当于V操作 ReleaseMutex(h_mutex_reader_wait);//离开,相当于V操作 printf("%s ",thread_name);Sleep(((TEST_INFO *)data)->persist_time*1000);//用延迟相应秒来模拟写文件操作WaitForSingleObject(h_mutex_read_count,-1);//申请进入,相当于P操作read_count--;if(read_count==0)LeaveCriticalSection(&CS_DATA);ReleaseMutex(h_mutex_read_count);//离开,相当于V操作
}//写者优先时的写者线程
void WF_writer_thread(void *data){Sleep(((TEST_INFO *)data)->require_moment*1000);//用延迟相应秒来模拟写文件操作WaitForSingleObject(h_mutex_write_count,-1);if(write_count==0)WaitForSingleObject(h_mutex_first_reader_wait,-1);write_count++;ReleaseMutex(h_mutex_write_count);//离开关于写者计数器的临界区相当于V操作EnterCriticalSection(&CS_DATA);printf("%s ",((TEST_INFO *)data)->thread_name);Sleep(((TEST_INFO *)data)->persist_time*1000);//用延迟相应秒来模拟写文件操作LeaveCriticalSection(&CS_DATA);WaitForSingleObject(h_mutex_write_count,-1); //申请进入,相当于P操作write_count--;if(write_count==0)ReleaseMutex(h_mutex_first_reader_wait);ReleaseMutex(h_mutex_write_count);//离开关于写者计数器的临界区相当于V操作
}//写者优先时的初始化程序
void writer_first(){int i=0;HANDLE h_thread[MAX_THREAD];printf("写优先申请次序:");for(i=0;i<MAX_THREAD;i++){printf("%s ",test_data[i].thread_name);}printf("\n");printf("写优先操作次序:");InitializeCriticalSection(&CS_DATA);for(i=0;i<MAX_THREAD;i++){if(test_data[i].thread_name[0]=='r')h_thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WF_reader_thread),&test_data[i],0,NULL);elseh_thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WF_writer_thread),&test_data[i],0,NULL);}WaitForMultipleObjects(MAX_THREAD,h_thread,TRUE,-1);printf("\n");
}//主程序
int main(int argc,char *argv[]){char select;while(1){printf("|-----------------------------------|\n");printf("|  1:reader first          |\n");printf("|  2:first come first served |\n");printf("|  3:writer first           |\n");printf("|  4:exit                 |\n");printf("|-----------------------------------|\n");printf("select a function(1~4):");do{select=(char)getch();}while(select!='1'&&select!='2'&&select!='3'&&select!='4');system("cls");switch(select){case '1':reader_first();break;case '2':first_come_first_served();break;case '3':writer_first();break;case '4':return 0;}printf("\nPress any key to continue.");getch();system("cls");}return 0;
}

【实验结果】

图1 初始界面

图2 读优先

图3 无优先

图4 写优先

【实验心得】

通过本次实验,我对操作系统的同步机制---读者写者问题有了更加深刻的认识,实验中也学到了很多知识,受益匪浅。读者写者问题在计算机领域非常普遍,数据库的读写分离就是为了减少因为读者写者问题加锁带来的对并发的影响。

读者写者问题的解决方案一般都有两种不同的侧重:读者优先或者写者优先。简单来说,读者优化就是尽量满足并发的读操作,当已经有线程在读数据的时候,其他读线程无需等待,而写线程需要等待所有正在进行的读操作之后才能执行。写者优先就是尽量满足写操作,尽管写操作不能并发,但是可以排队,优先于等待的读线程获得执行权。

同步机制—读者写者问题相关推荐

  1. 信号量机制——读者-写者问题

    信号量机制--读者-写者问题 问题描述 一个共享数据区,有若干个进程负责对其进行读入工作,若干个进程负责对其进行写入工作. 允许多个进程同时读数据 互斥读数据 若有进程写数据,不允许读者读数据 对照生 ...

  2. 《OS:PV操作 - 读者写者问题》

    经典同步问题 读者写者问题 背景:有多个读者,也有多个写者.要保证其互斥性.要求应用对应的PV操作实现. 定义: P,V操作均为原子操作 原子操作是在执行的过程中,要么全做,要么全不做,不可被CPU打 ...

  3. 操作系统【信号量集机制、“读者-写者”问题】

    0.信号量机制应用引导篇.flv 1.信号量集机制解决读写问题深入分析.flv   03:00 2."读者-写者"问题之"顺序执行".flv   07:32 3 ...

  4. 【操作系统/OS笔记14】经典同步问题:读者-写者问题、哲学家就餐问题

    本次笔记内容: 10.6 经典同步问题-1 10.7 经典同步问题-2 10.8 经典同步问题-3 10.9 经典同步问题-4 10.10 经典同步问题-5 10.11 经典同步问题-6 文章目录 读 ...

  5. 锁机制:读者写者问题 Linux C

    最近碰到一些锁机制的问题,想起大三的时候做过的一个小课设,记录复习一下. 问题描述: 一个数据文件可以被多个进程共享,其中,有些进程要求读(reader进程),而另一些进程要求对数据进行写或修改(wr ...

  6. 操作系统(四) | 经典进程的同步问题(生产者--消费者问题、哲学家进餐问题、读者--写者问题)

    文章目录 生产者--消费者问题 分析 实现 哲学家进餐问题 方法一:最多4人同时拿左筷子,最终保证一人能进餐 方法二:同时给左右筷子 解法1:AND信号量 解法2:信号量保护机制 方法三:让奇数先左后 ...

  7. 经典同步问题三——读者写者问题

    系列同步问题: 经典同步问题一--生产者和消费者问题 https://blog.csdn.net/weixin_36465540/article/details/105560002 经典同步问题二-- ...

  8. 操作系统 --经典同步问题之吸烟者问题读者-写者问题(七)

    一.吸烟者问题 1.问题描述 假设一个系统有三个抽烟者进程和一个供应者进程.每个抽烟者不停地卷烟并抽掉,但是要卷起并抽掉一支烟,需要三种材料:烟草.纸.胶水.三个抽烟者中,每一个第一个拥有烟草,第二个 ...

  9. 信号量机制实现读者写者问题(思路剖析+Java代码实现+验证)

    写在前面: Java中: 我们用这样的代码新建一个信号量:Semaphore mutex = new Semaphore(1); P操作(wait)的代码为:mutex.acquire(); V操作( ...

最新文章

  1. C 语言编程利器 之CLion
  2. neutron linux网络命令,OpenStack Neutron网络组件介绍(重要)
  3. CSS 中功能相似伪类间的区别
  4. python安装缺少api怎么办_请问缺少win32api模块该如何解决?
  5. python自动化办公模块_Python 自动化办公之 Excel 模块 — openpyxl 的基本使用!
  6. node.js(四)Mongoose使用进阶
  7. MSDN Library Visual Studio6.0 简体中文版下载及安装
  8. PSP3000/2000V3用5.03GEN-C安装教程
  9. iOS 录音及播放 音波图波形
  10. 频域波束形成matlab,关于FFT波束形成
  11. 从零开始SQL注入之二
  12. 多臂老虎机(Multi-armed Bandit)MAB学习笔记
  13. input标签 设置纯数字输入
  14. WinForm通过Excel作为中间介质实现导入导出小工具
  15. python爬虫打造_Python爬虫与AI结合,打造诗歌接龙程序!
  16. 记free 多次引发的内存踩踏事件
  17. Win32汇编练习(SMU—循环结构)
  18. Libev源码分析02:Libev中的IO监视器
  19. [词性] 二十三、情态动词 2 [ have to ] [ ought to ] [ dare ] [ be able to ] [ needn‘t ] [ had better ]
  20. 如何有效的激励员工?这本员工激励书籍推荐给你

热门文章

  1. Git 常用命令速查表(收藏大全)
  2. 使用蓝叠模拟安卓系统安装apk文件
  3. C++ opencv模板匹配
  4. 仿闲鱼验货宝链接源码
  5. 【AI CLUB】机器学习基础丨01
  6. 更改INSM地址的操作
  7. RFID(射频识别)
  8. 约瑟夫问题的几种解决方法
  9. html下拉列表“省市关联列表”js和jq两种方法实现
  10. C#中如何获取一个字体的宽度值(像素单位)-获得文字的像素宽度