读者写者问题
一、设计要求:
在 Windows2000 环境下,创建一个控制台进程,此进程包含 n 个线程。用这 n 个线程来表示 n 个读者或写
者。每个线程按相应测试数据文件(后面介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优
先问题。
读者-写者问题的读写操作限制(包括读者优先和写者优先)
1) 写-写互斥:不能有两个写者同时进行写操作
2) 读-写互斥:不能同时有一个线程在读,而另一个线程在写。
3) 读-读允许:可以有一个或多个读者在读。
读者优先的附加限制:如果读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读
操作。
写者优先的附加限制:如果一个读者申请进行读操作时已有另一个写者在等待访问共享资源,则该读者必须
等到没有写者处于等待状态后才能开始读操作。
运行结果显示要求:要求在每个线程创建、发出读写申请、开始读写操作和结束读写操作时分别显示一行提
示信息,以确定所有处理都遵守相应的读写操作限制。
二、测试数据文件格式:
测试数据文件包括 n 行测试数据,分别描述创建的 n 个线程是读者还是写者,以及读写操作的开始时间和持
续时间。每行测试数据包括四个字段,每个字段间用空格分隔。第 1 个字段为正整数,表示线程的序号。第 2
个字段表示线程的角色,R 表示读者,W 表示写者。第 3 个字段为一个正数,表示读写开始时间:线程创建后,
延迟相应时间(单位为秒)后发出对共享资源的读写申请。第 4 个字段为一个正数,表示读写操作的延迟时间。
当线程读写申请成功后,开始对共享资源进行读写操作,该操作持续相应时间后结束,释放该资源。
下面是一个测试数据文件的例子(在记事本手工录入数据):
1 R 3 5
2 W 4 5
3 R 5 2
4 R 6 5
5 W 5.1 3
三、实习分析:
可以将所有读者和所有写者分别存于一个读者等待队列和一个写者等待队列中,每当读允许时,就从读者队
列中释放一个或多个读者线程进行读操作;每当写允许时,就从写者队列中释放一个写者线程进行写操作。
1. 读者优先:
读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整数变量 Read_count 记录当
前的读者数目,用于确定是否需要释放正在等待的写者进程(当 Read_count=0 时,表明所有的读者读完,需要
释放写者等待队列中的一个写者)。每当一个读者开始读文件时,必须修改 Read_count 变量。因此需要一个互斥
对象 mutex 来实现对全局变量 Read_count 修改时的互斥。
另外,为了实现写-写互斥,需要增加一个临界区对象 Write。当写者发出写请求时,必须申请临界区对象的
所有权。通过这种方法,可以实现读-写互斥,当 Read_count=1 时(即第一个读者到来时),读者线程也必须申
请临界区对象的所有权。
当读者拥有临界区的所有权时,写者阻塞在临界区对象 Write 上。当写者拥有临界区的所有权时,第一个读
者判断完”Read_count==1”后阻塞在 Write 上,其余的读者由于等待对 Read_count 的判断,阻塞在 mutex 上。
2. 写者优先:
写者优先与读者优先相类似。不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个
写者在等待,则新到来的读者不允许进行读操作。为此应当填加一个整形变量 Write_count,用于记录正在等待
的写者的数目,当 Write_count=0 时,才可以释放等待的读者线程队列。
为了对全局变量 Write_count 实现互斥,必须增加一个互斥对象 mutex3。
为了实现写者优先,应当填加一个临界区对象 read,当有写者在写文件或等待时,读者必须阻塞在 read 上。
读者线程除了要对全局变量 Read_count 实现操作上的互斥外,还必须有一个互斥对象对阻塞 read 这一过程
实现互斥。这两个互斥对象分别命名为 mutex1,mutex2。
(请同学们参考 MSDN)
四、相关 WindowsAPI 说明:
1. CreateThread: 创建一个在调用进程的地址空间中执行的线程。
2. ExitThread
3. Sleep: 对指定的时间间隔挂起当前的执行线程
4. CreateMutex: 创建有名或者无名的互斥对象
5. ReleaseMutex:
6. WaitForSingleObject: 当发生(1)指定对象处于信号态(2)超时 则该函数返回
7. WaitForMultipleObject: 任意一个或全部指定对象处于信号态或超时间隔已过时,返回
8. CreateSemapore: 创建一个有名或无名信号对象。
9. ReleaseSemapore:
10. InitializeCriticalSection: 初始化临界区对象
11. EnterCriticalSection: 等待指定临界区对象的所有权。当调用线程被赋予所有权时,返回。
12. LeaveCriticalSection:该函数释放指定临界区对象的所有权。
WinSock 网络通讯
一、实习要求
通过 WinSock 编程实现网络通讯。具体形式为面向连接的 TCP 客户机-服务器模式:在两台机器(在一台机
器上也可以模拟)依次启动服务器、客户端。在客户端输入正确的服务器地址后确认连接是否成功,若成功则可
以在客户端和服务器之间互相发送数据,并可用 bye 退出。同时,客户端与服务器应该能实时地检测到对方的运
行状态,并且对特定的状态做出正确的反映行为(如:假设服务器在检测到客户端退出后,应当自动给出信息,
并关闭此次连接,准备接受下一次连接)。
二、实习环境
在 VC++使用 Winsock 库,选择 Project->Setting->Link, 在 Object/library modules 一栏中填上 wsock32.lib。
三、实习步骤
服务器和客户端需要各自开辟一个线程完成接受工作。接受线程同时不停地检测发送端的状态,一旦发送端
退出则作出相应的反映。主进程则等待用户在控制台(Console)上的输入,若不是 bye 则把信息发送给接受端,
若是 bye,则退出连接。
四、相关函数
1.WSAStartup
2.WSAGetLastError
3.bind
4.listen
5.accept 6.connect
7. send
8.recv
9.closesocket
五、难易程度
1.易:实习最简单的 SOCKET API 函数
2.中:实现一对一连接,然后再实现多个客户端对一个服务器的连接形式
3.中上:无连接通讯方式
4.中上:Winsock 有两中 I/O 方式,锁定和非锁定,了解它们的机制,比较其不同点,用两种方式分别完成设
计要求
5.难:什么是“多播”,实现 ATM 多播实例
6.极难:了解常规服务质量(Quality of Service, QoS)的概念和 QoS 的编程方法。
// 课程设计 1:已经写出读者优先的程序,请设计出写者优先的程序
//ReadWrite.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#include
#include
#include "fstream.h"
#include
#include
#include
#define READER 'R'
#define WRITER 'W'
#define INTE_PER_SEC 100
//每秒时钟中断数目
#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;
};
//线程序号
//线程类别
//线程开始时间
//线程读写持续时间
//读者优先处理函数,已经给出
void ReaderPriority(char* file);
void RP_ReaderThread(void *p);
void RP_WriterThread(void *p);
//写者优先处理函数,精读“读者优先”程序和设计分析,自己完成
void WriterPriority(char* file);
void WP_ReaderThread(void *p);
void WP_WriterThread(void *p);
//为了保证现在程序编译通过,写一个空函数,请你写出自己的 WriterPriority
void WriterPriority(char* file){}
int main()
{
char ch;
while (true)
{
1:Reader Priority\n");
2:Writer Priority\n");
3:Exit to Windows\n");
printf("*********************************************************\n");
printf("
printf("
printf("
printf("*********************************************************\n");
printf("Enter your choice(1, 2 or 3\n");
do
{
ch=(char) _getch();
}while (ch!='1' && ch!='2' && ch!='3');
system("cls");
if (ch=='3')
return 0;
else if (ch=='1')
ReaderPriority("thread.dat");
else
WriterPriority("thread.dat");
printf("\nPress Any key to continue.\n");
_getch();
system("cls");
}
return 0;
}
//读者优先处理函数
void ReaderPriority(char* file)
{
//等待所有线程结束
//线程数目
//线程 ID
DWORD n_thread=0;
DWORD thread_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(file);
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;
n_thread++;
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),&threa
d_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");
}
//读者优先-----读者线程
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);
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
readcount++;
if (readcount==1)
//等待互斥信号,保证对 readcount 的访问、修改互斥
//读者数目增加
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);
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
readcount--;
if (readcount==0)
//等待互斥信号,保证对 readcount 的访问、修改互斥
//读者数目减少
LeaveCriticalSection(&RP_Write);
//如果所有读者读完,唤醒写者
ReleaseMutex(h_Mutex);
}
//读者优先-----写者线程
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 file.\n",m_serial);
Sleep(m_persist);
//退出线程
printf("Writer thread %d finished writing file.\n",m_serial);
LeaveCriticalSection(&RP_Write);
//如果所有读者读完,唤醒写者
}
/* 数据文件 thread.dat 为
1 R 3 5
2 W 4 5
3 R 5 2
4 R 6 5
5 W 5.1 3 时,屏幕将显示下面的内容
Reader Priority:
Reader thread 1 sents the reading require.
Reader thread 1 begins to read file.
Writer thread 2 sents the Writing require.
Reader thread 3 sents the reading require.
Reader thread 3 begins to read file.
Writer thread 5 sents the Writing require.
Reader thread 4 sents the reading require.
Reader thread 4 begins to read file.
Reader thread 3 finished reading file.
Reader thread 1 finished reading file.
Reader thread 4 finished reading file.
Writer thread 2 begins to write file.
Writer thread 2 finished writing file.
Writer thread 5 begins to write file.
Writer thread 5 finished writing file.
All Reader and Writer have finished operating.
*/
// 课程设计 2:已经给出服务器端的程序,请设计出客户端的程序
//TCP_Server.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "iostream.h"
#include "winsock.h"
#define DEFAULT_PORT 4000
DWORD WINAPI ThreadFunc(LPVOID pParam);
DWORD WINAPI ReadIn(LPVOID pParam);
char info[255];
bool isConnect;
//默认端口号
//服务器端 socket 接收线程函数
//服务器端控制台(console)接受函数
//服务器端控制台输入字符串
//是否有客户端连接
int main()
{
//初始化 Winsock
WORD wVersionRequested=MAKEWORD(2,2);
WSADATA wsaData;
WSAStartup(wVersionRequested,&wsaData);
SOCKET sServSock=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr;
int nSockErr;
int nNumConns=0;
SOCKET sConn;
sockaddr ConnAddr;
int nAddrLen=sizeof(sockaddr);
addr.sin_family=AF_INET;
addr.sin_port=htons(DEFAULT_PORT);
addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//绑定套接字到一个已知地址
if(bind(sServSock,(LPSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR)
//当前连接数目
nSockErr=WSAGetLastError();
//将套接字置入监听模式
if (listen(sServSock,5)==SOCKET_ERROR)
nSockErr=WSAGetLastError();
cout << "*****************Wait Client to Connect**********************" << endl;
while (strcmp(info,"bye"))
{
//如果没有给出退出请求
cin.delbuf();
while (nNumConns<1)
{
//完成接受客户端连接准备
sConn=accept(sServSock,&ConnAddr,&nAddrLen);
if (sConn==INVALID_SOCKET) nSockErr=WSAGetLastError();
else
{
isConnect=true;
CreateThread(NULL,0,ThreadFunc,&sConn,0,NULL);
//创建服务器的接受线程
cout << "*****************Accpet successfull.*****************"<
}
}
if (nNumConns>=1)
ReadIn(&sConn);
//处理控制台输入并执行相应操作
nNumConns=0;
closesocket(sConn);
}
return 0;
}
//服务器接受函数
DWORD WINAPI ThreadFunc(LPVOID pParam)
{
SOCKET *s=(SOCKET *)pParam;
char in[255];
while(strcmp(info,"bye"))
{
//退出
if(recv(*s,in,255,0)!=SOCKET_ERROR)
{
//接受成功
cout << "\b" << in << endl << ">";
cout.flush();
//接受失败,客户端退出
cout<<"*****************Client Exit*****************\n";
cout<<"*****************Press Enter*****************"<";
cin.getline(info,255);
while(strcmp(info,"bye")&&isConnect)
{
send(*sConn,info,255,0);
cout << ">";
cin.getline(info,255);
//输入不是"bye"就 send
}
isConnect=false;
cout<<"*****************Wait Another*****************"<