一、实验目的
1、回顾操作系统进程、线程的有关概念,针对经典的同步、互斥、死锁与饥饿问题进行并发程序设计。
2、了解互斥体对象,利用互斥与同步操作编写读者-写者问题的并发程序,加深对P、V原语以及利用P、V原语进行进程间同步与互斥操作的理解。
二、基本原理
1、CreateSemaphore()函数,其参数含义分别为:
①定义信号量的安全特性 ②信号量的初始计数
③信号量的最大计数 ④信号量对象的名称
2、CreateThread()函数,其参数含义分别为:
①定义信号量的安全特性 ②初始栈的大小
③指向线程函数的指针 ④向线程函数传递的参数
⑤线程标志 ⑥保存新线程的ID
3、WaitForSingleObject()函数,其参数含义分别为:
①对象句柄(可指定一系列对象) ②定时时间间隔
4、ReleaseSemaphore()函数,参数含义为所要操作的信号量对象的句柄。
三、模块介绍
1、main()函数模块:初始化读者和写者的信息并创建线程;
2、Read()函数模块:读者阅读;
3、Write()函数模块:写者写入;
4、Leave()函数模块:读者/写者离开;
5、Reader()函数模块:读者线程执行互斥访问操作;
6、Writer()函数模块:写者线程执行互斥访问操作。
四、流程图
五、代码实现
#include<iostream>
#include<Windows.h>
using namespace std;
unsigned short writerID = 1; //写者的ID
int readcount = 0; //记录读者的数量
bool p_ccontinue = true; //控制程序结束
HANDLE x; //确保readcount被正确地更新
HANDLE wsem; //实现读进程(写进程)的互斥
DWORD WINAPI Reader(LPVOID); //读者线程
DWORD WINAPI Writer(LPVOID); //写者线程
int main() {
const unsigned short READERS_COUNT = 3; //读者总数
const unsigned short WRITERS_COUNT = 2; //写者总数
const unsigned short THREADS_COUNT = READERS_COUNT + WRITERS_COUNT; //线程总数
HANDLE hThreads[THREADS_COUNT]; //各线程的handle
DWORD ReaderID[READERS_COUNT]; //读者线程的标识符
DWORD WriterID[WRITERS_COUNT]; //写者线程的标识符
wsem = CreateSemaphore(NULL, 1, READERS_COUNT, NULL);
x = CreateSemaphore(NULL, 1, READERS_COUNT, NULL);
for (int i = 0; i < READERS_COUNT; ++i) { //创建读者线程
hThreads[i] = CreateThread(NULL, 0, Reader, NULL, 0, &ReaderID[i]);
if (hThreads[i] == NULL)
return -1;
}
for (int i = 0; i < WRITERS_COUNT; ++i) { //创建写者进程
hThreads[WRITERS_COUNT + i] = CreateThread(NULL, 0, Writer, NULL, 0, &WriterID[i]);
if (hThreads[i] == NULL)
return -1;
}
while (p_ccontinue)
if (getchar()) //按回车后终止程序运行
p_ccontinue = false;
return 0;
}
void Read() { cout << "Readers start reading..." << endl; } //读者开始阅读
void Write() { cout << "Writers " << writerID++ << " start writing..." << endl; } //写者开始写入
void Leave() { cout << "leaving..." << endl; } //离开
DWORD WINAPI Reader(LPVOID lpPara) { //读者
while (p_ccontinue) {
WaitForSingleObject(x, INFINITE); //semwait(x)[INFINITE:对象被触发信号后函数返回]
readcount++;
if (readcount == 1)
WaitForSingleObject(wsem, INFINITE); //semwait(wesm)[INFINITE:对象被触发信号后函数返回]
ReleaseSemaphore(x, 1, NULL); //semsignal(x)
Read();
Sleep(2500);
WaitForSingleObject(x, INFINITE); //semwait(x)[INFINITE:对象被触发信号后函数返回]
Leave();
readcount--;
if (readcount == 0)
ReleaseSemaphore(wsem, 1, NULL); //semsignal(wsem)
ReleaseSemaphore(x, 1, NULL); //semsignal(x)
}
return 0;
}
DWORD WINAPI Writer(LPVOID lpPara) { //写者
while (p_ccontinue) {
WaitForSingleObject(wsem, INFINITE); //semwait(wesm)
Write();
Sleep(1500);
Leave();
ReleaseSemaphore(wsem, 1, NULL); //semsignal(wsem)
}
return 0;
}
六、实验结果与分析
运行结果如下
本实验选择读者优先的策略,首先前三个读者依次申请进行读取操作,其中第一个读者申请后执行semwait(wsem),禁止写者进行写入操作,此后读者读取数据。当第三个读者离开时执行semsignal(wsem),允许写者进行写入操作;当第一个写者成功申请进行写入操作时执行semwait(wsem),禁止读者和其他写者进行操作,当第一个写者离开时执行semsignal(wsem),允许读者和其他写者进行操作,以此类推。