您好,欢迎来到爱go旅游网。
搜索
您的当前位置:首页操作系统试验

操作系统试验

来源:爱go旅游网
实验1 进程控制

实验内容:

在windows系统下通过有关进程的系统调用,实现进程的简单操作,并观察程序的运行情况,通过阅读和分析实验程序,分析执行结果。这些加深对进程概念的理解,明确进程与程序之间的区别。

实验目的:

通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解Windows进程的“一生”,并了解学习创建进程、观察进程和终止进程的程序设计方法。

实验要求:

(1)理解系统调用CreateProcess()的功能。

(2)编译并运行给出的程序,观察和分析程序执行的结果,给出需要回答的问题。

(3)有多余时间则编写程序,实现输入任意执行文件并启动和选择结束进程功能的程序。 实验内容:

1. 创建进程

本实验显示了创建子进程的基本框架。程序只是再一次地启动自身,并显示它的系统进程ID和它在进程列表中的位置。

步骤1:编译并执行1-1.exe程序, 完成下列问题。

程序1-1是一个简单的使用CreateProcess() API函数的例子。首先形成简单的命令行,提供当前的EXE文件的指定文件名和代表生成克隆进程的号码。大多数参数都可取缺省值,但是创建标志参数使用了:CREATE_NEW_CONSOLE 标志,指示新进程分配它自己的控制台,使得运行示例程序时,在任务栏上产生许多活动标记。然后该克隆进程的创建方法关闭传递过来的句柄并返回main()

函数。在关闭程序之前,每一进程的执行主线程暂停一下,以便让用户看到其中的至少一个窗口。

程序运行时屏幕显示的信息是: Process ID:20, Clone ID:0

如果将创建标志参数该为其它,请自己设置一个, 程序运行时屏幕显示的信息是:

Process ID:20, Clone ID:0

如果设置创建参数改为:NULL。则表示程序在一个控制台上显示。程序运行时屏幕最初显示的信息是:Process ID:244, Clone ID:0,随后在该控制台上显示程序运行的结果。

2. 正在运行的进程

本实验的程序中列出了用于进程信息查询的API函数GetProcessVersion() 与GetVersionEx() 的共同作用,可确定运行进程的操作系统的版本号。 步骤2:编译并执行1-2.exe程序, 完成下列问题。 执行1-2.exe程序的运行结果: 当前PID信息:20 当前操作系统版本:5.1

系统提示信息:Task Manager should now indicate thin process is high priorty. 清单1-2中的程序表明如何获得当前的PID和所需的进程版本信息。接着,程序演示了如何使用GetVersionEx() API函数来提取OSVERSIONINFOEX结构。这一数据块中包括了操作系统的版本信息。其中,“OS : 5.1”表示当前运行的操作系统是:Windows XP

清单1-2的最后一段程序利用了操作系统的版本信息,以确认运行的是Windows。代码接着将当前进程的优先级提高到比正常级别高。

步骤3:单击Ctrl + Alt + Del键,进入“Windows任务管理器”,在“应用程序”选项卡中右键单击“1-2”任务,在快捷菜单中选择“转到进程”命令。

在“Windows任务管理器”的“进程”选项卡中,与“2-2”任务对应的进程映像名称是 (为什么?) :与“1-2”任务对应的进程映像名称是VCSPAWN.EXE ,因为“1-2”在该程序下运行。

右键单击该进程名,在快捷菜单中选择“设置优先级”命令,可以调整该进程的优先级,如设置为“高”后重新运行1-2.exe程序,屏幕显示有变化吗?为什么? 屏幕显示没有变化。已经是高的优先级了。

3. 终止进程

在清单1-3列出的程序中,先创建一个子进程,然后指令它发出“自杀弹”互斥体去终止自身的运行。

清单1-3中的程序说明了一个进程从“生”到“死”的整个一生。第一次执行时,它创建一个子进程。在创建子进程之前,先创建一个互斥的内核对象,以便父子进程间进行协调。当创建子进程时,就打开了互斥体并在其他线程中进行别的处理工作,同时等待着父进程使用ReleaseMutex() 发出信号。然后用Sleep() 来模拟父进程处理其他工作,等完成时,指令子进程终止。

进程可使用退出代码将终止方式通知给调用GetExitCodeProcess() 的其他进程。同时,GetProcessTimes()函数可向主调者显示进程的终止时间。 步骤4:编译并执行1-3.exe程序, 其运行结果: 1) Creating the child process. 表示:正在创建一个子进程。 2)Telling the child process to quit. 表示:指令子进程终止。

步骤5:在熟悉清单1-3源代码的基础上,利用本实验介绍的API函数来尝试改进本程序 (例如使用GetProcessTimes()函数) 并运行。请描述你所做的工作:

程序1-1.cpp

// proc create项目 #include #include #include

#include

// 创建传递过来的进程的克隆过程并赋于其ID值 void StartClone(int nCloneID) {

// 提取用于当前可执行文件的文件名 TCHAR szFilename[MAX_PATH] ;

GetModuleFileName(NULL, szFilename, MAX_PATH) ;

// 格式化用于子进程的命令行并通知其EXE文件名和克隆ID TCHAR szCmdLine[MAX_PATH];

sprintf(szCmdLine,\"\\\"%s\\\" d%\

// 用于子进程的STARTUPINFO结构 STARTUPINFO si;

ZeroMemory(reinterpret_cast (&si) , sizeof(si) ) ; si.cb = sizeof(si) ; // 必须是本结构的大小

// 返回的用于子进程的进程信息 PROCESS_INFORMATION pi;

// 利用同样的可执行文件和命令行创建进程,并赋于其子进程的性质 BOOL bCreateOK=CreateProcess(

szFilename, // 产生这个EXE的应用程序的名称 szCmdLine, // 告诉其行为像一个子进程的标志 NULL, // 缺省的进程安全性 NULL, // 缺省的线程安全性 FALSE, // 不继承句柄 NULL,

//CREATE_NEW_CONSOLE, // 使用新的控制台 标志参数 NULL, // 新的环境 lpEnvironment参数,指明为新进程提供的环境 NULL, // 当前目录 lpCurrentDirectory,可用于向主创进程发送与缺省目录不同的新进程使用的特殊的当前目录

&si, // 启动信息 STARTUPINFO数据结构所必需的,用于在必要时指明新应用程序的主窗口的外观。

&pi) ; // 返回的进程信息 用于新进程对象及其主线程的句柄和ID的返回值缓冲区。

// 对子进程释放引用 if (bCreateOK) {

CloseHandle(pi.hProcess) ; CloseHandle(pi.hThread) ; } }

int main(int argc, char* argv[] ) {

// 确定进程在列表中的位置 int nClone(0) ; if (argc > 1) {

sscanf(argv[1] , \"%d\" , &nClone) ;// 从第二个参数中提取克隆ID }

// 显示进程位置

cout << \"Process ID:\" << GetCurrentProcessId() << \

//Sleep(500) ; Sleep(4000);

// 检查是否有创建子进程的需要 const int c_nCloneMax=25; if (nClone < c_nCloneMax) {

StartClone(nClone) ;// 发送新进程的命令行和克隆号 }

// 在终止之前暂停一下 (l/2秒) //Sleep(200) ; Sleep(4000);

return 0; }

程序1-2.cpp

// version项目 #include #include #include

// 利用进程和操作系统的版本信息的简单示例 void main() {

// 提取这个进程的ID

DWORD dwIdThis=GetCurrentProcessId();

// 获得这一进程和报告所需的版本,也可以发送0以便指明这一进程 DWORD dwVerReq= GetProcessVersion(dwIdThis); WORD wMajorReq=(WORD)(dwVerReq>16) ; WORD wMinorReq=(WORD)(dwVerReq & 0xffff) ;

cout << \"Process ID: \"<< dwIdThis

<<\ << wMinorReq << endl ;

// 设置版本信息的数据结构,以便保存操作系统的版本信息 OSVERSIONINFOEX osvix; ZeroMemory(&osvix, sizeof(osvix) ) ; osvix.dwOSVersionInfoSize=sizeof(osvix) ;

// 提取版本信息和报告

GetVersionEx(reinterpret_cast < LPOSVERSIONINFO > (&osvix)) ; cout << \"Running on OS:\" << osvix.dwMajorVersion <<\".\" << osvix.dwMinorVersion << endl;

// 如果是NTS(Windows) 系统,则提高其优先权 if

(osvix.dwPlatformId==VER_PLATFORM_WIN32_NT

osvix.dwMajorVersion >= 5) {

// 改变优先级 SetPriorityClass(

GetCurrentProcess() , // 进程的ID号 HIGH_PRIORITY_CLASS); // 改变为high

&&

// 报告给用户

cout << \"Task Manager should now indicate this \" \"process is high priority.\"<< endl; } getchar(); }

程序1-3.cpp

// procterm项目 # include # include # include #include

static LPCTSTR g_szMutexName = \"w2kdg.ProcTerm.mutex.Suicide\" ;

// 创建当前进程的克隆进程的简单方法 void StartClone() {

// 提取当前可执行文件的文件名 TCHAR szFilename[MAX_PATH] ;

GetModuleFileName(NULL, szFilename, MAX_PATH) ;

// 格式化用于子进程的命令行,指明它是一个EXE文件和子进程 TCHAR szCmdLine[MAX_PATH] ;

sprintf(szCmdLine, \"\\\"%s\\\"child\" , szFilename) ;

// 子进程的启动信息结构 STARTUPINFO si;

ZeroMemory(reinterpret_cast (&si),sizeof(si)) ;

si.cb = sizeof(si) ; // 应当是此结构的大小

// 返回的用于子进程的进程信息 PROCESS_INFORMATION pi;

// 用同样的可执行文件名和命令行创建进程,并指明它是一个子进程 BOOL bCreateOK=CreateProcess(

szFilename, // 产生的应用程序的名称 (本EXE文件) szCmdLine, // 告诉我们这是一个子进程的标志 NULL, // 用于进程的缺省的安全性 NULL, // 用于线程的缺省安全性 FALSE, // 不继承句柄 NULL,

//CREATE_NEW_CONSOLE, // 创建新窗口 NULL, // 新环境 NULL, // 当前目录 &si, // 启动信息结构 &pi ) ; // 返回的进程信息

// 释放指向子进程的引用 if (bCreateOK) {

CloseHandle(pi.hProcess) ; CloseHandle(pi.hThread) ; } }

void Parent() {

// 创建互斥体

HANDLE hMutexSuicide= CreateMutex( NULL, // 缺省的安全性 TRUE, // 最初拥有的 g_szMutexName) ; // 为其命名 if (hMutexSuicide != NULL) {

// 创建子进程

cout << \"Creating the child process.\" << endl; StartClone() ;

// 暂停 Sleep(5000) ;

// 指令子进程自己终止

cout << \"Telling the child process to quit. \"<< endl; ReleaseMutex(hMutexSuicide) ;

// 消除句柄

CloseHandle(hMutexSuicide) ; } }

void Child() {

// 打开互斥体

HANDLE hMutexSuicide = OpenMutex( SYNCHRONIZE, // 打开用于同步

FALSE, // 不需要向下传递 g_szMutexName) ; // 名称 if (hMutexSuicide != NULL) {

// 报告我们正在等待指令

cout <<\"Child waiting for suicide instructions. \" << endl; WaitForSingleObject(hMutexSuicide, INFINITE) ;

// 准备好终止,清除句柄 cout << \"Child quiting.\" << endl; CloseHandle(hMutexSuicide) ;

Sleep(5000) ; } }

int main(int argc, char* argv[] ) {

// 决定其行为是父进程还是子进程

HANDLE hMutexSuicide; GetProcessTimes(hMutexSuicide); if (argc > 1 && strcmp(argv[1] , \"child\" )== 0) {

Child() ; } else {

Parent() ; }

return 0; }

实验2 经典同步问题实践 实验内容:

学习Windows有关进程/线程同步的背景知识和API,分析2个实验程序(事件对象在进程间传送信号的应用和利用互斥体和临界区实现读者写者问题),观察程序的运行情况并分析执行结果。 实验目的:

在本实验中,通过对事件和互斥体对象的了解,来加深对Windows 线程同步的理解。

(1) 了解事件和互斥体对象。

(2) 通过分析实验程序,理解管理事件对象的API。 (3) 理解在进程中如何使用事件对象。

(4) 理解在进程中如何使用互斥体和临界区对象。 (5) 理解父进程创建子进程的程序设计方法。 实验要求:

(1) 理解Windows有关进程/线程同步的背景知识和API。

(2) 按要求运行2个程序,观察程序执行的结果,并给出要求的结果分析。 (3) 参照2-2程序,写出一个实现单个生产者—消费者问题的算法,可以使用单个缓冲区,也可以使用缓冲池,生产者随机产生任意形式的数据并放入缓冲区中,消费者则以随机的时间间隔从缓冲区中取数据,随机时间请使用随机数产生。

并发与同步的背景知识

Windows开发人员可以使用线程同步对象来协调线程和进程的工作,以使其共享信息并执行任务。此类对象包括互锁、临界区、事件、互斥体和信号等。 多线程编程中关键的一步是保护所有的共享资源,工具主要有互锁函数、临界段

和互斥体等;另一个是协调线程使其完成应用程序的任务,为此,可利用内核中的事件对象和信号。

在进程内或进程间实现线程同步的最方便的方法是使用事件对象,这组内核对象允许一个线程对其受信状态进行直接控制。

而互斥体则是另一个可命名且安全的内核对象,主要目的是引导对共享资源的访问。拥有单一访问资源的线程创建互斥体,所有希望访问该资源的线程应该在实际执行操作之前获得互斥体,而在访问结束时立即释放互斥体,以允许下一个等待线程获得互斥体,然后接着进行下去。

与事件对象类似,互斥体容易创建、打开、使用并清除。利用CreateMutex() API可创建互斥体,创建时可以指定一个初始的拥有权标志,通过使用这个标志,只有当线程完成了资源的所有的初始化工作时,才允许创建线程释放互斥体。

表2-1 用于管理事件对象的API

API名称 描述 在内核中创建一个新的事件对象。此函数允许有安全CreateEvent() 性设置、手工还是自动重置的标志以及初始时已接受还是未接受信号状态的标志 OpenEvent() SetEvent() ResetEvent() PulseEvent()

为了获得互斥体,首先,想要访问调用的线程可使用OpenMutex() API来获得指向对象的句柄;然后,线程将这个句柄提供给一个等待函数。当内核将互斥体对象发送给等待线程时,就表明该线程获得了互斥体的拥有权。当线程获得拥有权时,线程控制了对共享资源的访问——必须设法尽快地放弃互斥体。放弃共享资源时需要在该对象上调用ReleaseMutex() API。然后系统负责将互斥体拥有权传

创建对已经存在的事件对象的引用。此API函数需要名称、继承标志和所需的访问级别 将手工重置事件转化为已接受信号状态 将手工重置事件转化为非接受信号状态 将自动重置事件对象转化为已接受信号状态。当系统释放所有的等待它的线程时此种转化立即发生 递给下一个等待着的线程 (由到达时间决定顺序) 。

实验内容与步骤 1. 事件对象

清单2-1程序展示如何在进程间使用事件。

父进程启动时,利用CreateEvent() API创建一个命名的、可共享的事件和子进程,然后等待子进程向事件发出信号并终止父进程。

在创建时,子进程通过OpenEvent() API打开事件对象,调用SetEvent() API使其转化为已接受信号状态。两个进程在发出信号之后几乎立即终止。

清单2-1 创建和打开事件对象在进程间传送信号 // event项目

# include # include # include

// 以下是句柄事件。实际中很可能使用共享的包含文件来进行通讯 static LPCTSTR g_szContinueEvent =\"w2kdg.EventDemo.event.Continue\";

// 本方法只是创建了一个进程的副本,以子进程模式 (由命令行指定) 工作 BOOL CreateChild() {

// 提取当前可执行文件的文件名 TCHAR szFilename[MAX_PATH] ;

GetModuleFileName(NULL, szFilename, MAX_PATH) ;

// 格式化用于子进程的命令行,指明它是一个EXE文件和子进程 TCHAR szCmdLine[MAX_PATH] ;

sprintf(szCmdLine, \"\"%s\"child\" , szFilename) ;

// 子进程的启动信息结构 STARTUPINFO si;

ZeroMemory(reinterpret_cast(&si), sizeof(si)) ; si.cb = sizeof(si); // 必须是本结构的大小

// 返回的子进程的进程信息结构 PROCESS_INFORMATION pi;

// 使用同一可执行文件和告诉它是一个子进程的命令行创建进程 BOOL bCreateOK = CreateProcess( szFilename, // 生成的可执行文件名

szCmdLine, // 指示其行为与子进程一样的标志 NULL, // 子进程句柄的安全性 NULL, // 子线程句柄的安全性 FALSE, // 不继承句柄 0, // 特殊的创建标志 NULL, // 新环境 NULL, // 当前目录 &si, // 启动信息结构 &pi ) ; // 返回的进程信息结构

// 释放对子进程的引用 if (bCreateOK) {

CloseHandle(pi.hProcess); CloseHandle(pi.hThread); }

return(bCreateOK) ; }

// 下面的方法创建一个事件和一个子进程,然后等待子进程在返回前向事件发出信号

void WaitForChild() {

HANDLE hEventContinue = CreateEvent(

NULL, // 缺省的安全性,子进程将具有访问权限 TRUE, // 手工重置事件

FALSE, // 初始时是非接受信号状态 g_szContinueEvent); // 事件名称 if (hEventContinue != NULL) {

cout << \"事件对象已经建立 „„ \" << endl; // 创建子进程

if ( CreateChild()) {

cout << \" 建立了一个子进程 \" << endl;

// 等待,直到子进程发出信号 cout << \"Parent waiting on child.\" << endl;

WaitForSingleObject(hEventContinue, INFINITE); Sleep(1500); // 删除这句试试,或者改变数值 cout << \"parent received the envent signaling from child\" << endl; }

// 清除句柄

CloseHandle(hEventContinue);

hEventContinue=INVALID_HANDLE_VALUE; } }

// 以下方法在子进程模式下被调用,其功能只是向父进程发出终止信号 void SignalParent() {

// 尝试打开句柄

cout << \"child process begining......\" << endl; HANDLE hEventContinue = OpenEvent(

EVENT_MODIFY_STATE, // 所要求的最小访问权限 FALSE, // 不是可继承的句柄 g_szContinueEvent); // 事件名称 if(hEventContinue != NULL) {

SetEvent(hEventContinue); cout << \"event signaled\" << endl; }

// 清除句柄

CloseHandle(hEventContinue) ;

hEventContinue = INVALID_HANDLE_VALUE; }

int main(int argc, char* argv[] ) {

// 检查父进程或是子进程是否启动

if (argc>1 && strcmp(argv[1] , \"child\" )== 0) {

// 向父进程创建的事件发出信号 SignalParent() ; } else

{

// 创建一个事件并等待子进程发出信号 WaitForChild(); Sleep(1500);

cout << \"Parent released.\" << endl ; } return 0; }

步骤1:编译并执行2-1.exe程序。

程序运行结果是 (分行书写) 1.事件对象已经建立 „„ 2.建立了一个子进程 3.Parent waiting on child. 4.child process begining...... 5.event signaled

6.parent received the envent signaling from child 7.Parent released.

阅读和分析程序2-1,请回答:

(1) 程序中,创建一个事件使用了哪一个系统函数?创建时设置的初始信号状态是什么?CreateEvent()函数;FALSE 代表无信号状态 (2) 创建一个进程 (子进程) 使用了哪一个系统函数? CreateChild()函数中的CreateProcess()API函数

(3) 从步骤1的输出结果,对照分析2-1程序,能够看出程序运行的流程吗?请简单描述:

Main()检查父进程或子进程是否启动,选择相应入口。然后进入waitforchild(),创建事件,再创建子进程CreateChild()并一直等待直到子进程发出信号。与此同时,子进程完成创建进入SignalParent() 函数,打开句柄向父进程传出信号,最

后清除句柄。

步骤2:编译程序生成执行文件2-1.exe,在命令行状态下执行程序,分别使用格式:

(1) 2-1 child (2) 2-1 或2-1 ***

运行程序,记录执行的结果,并分行说明产生不同结果的原因。

E:\\操作系统\\实验3 同步机制(2次)\\Debug>2-1 child child process begining......

原因:执行Child只是完成该进程SignalParent()函数,甚至都打不开OpenEvent()而终止

E:\\操作系统\\实验3 同步机制(2次)\\Debug>2-1 事件对象已经建立 „„ 建立了一个子进程 Parent waiting on child. child process begining...... event signaled

parent received the envent signaling from child Parent released. 2. 互斥体对象

清单2-2的程序中是读者写者问题的一个实现,满足读者优先原则,使用同步机制的互斥体和临界区,对每个读者和写者分别用一个线程来表示。

测试数据文件的数据格式说明:

测试数据文件包括n行测试数据,每行描述创建的是用于产生读者还是写者的数据。每行测试数据包括4个字段,各字段间用空格分隔。   

第一字段为线程序号。

第二字段表示相应线程角色,W表示写者,R表示读者。 第三字段为线程延迟。

第四字段为线程读写操作持续时间。

清单2-2 利用互斥体和临界区实现读者写者问题 #include \"windows.h\" #include #include #include #include #include #include

#define READER 'R' // 读者 #define WRITER 'W' // 写者

#define INTE_PER_SEC 1000 // 每秒时钟中断数目 #define MAX_THREAD_NUM // 最大线程数目 #define MAX_FILE_NUM 32 // 最大数据文件数目 #define MAX_STR_LEN 32 // 字符串长度 int readcount = 0; // 读者数目 CRITICAL_SECTION RP_Write; // 定义临界区

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\

//等待互斥信号,保证对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\Sleep(m_persist) ;

// 退出线程

printf(\"Reader thread %d finished reading file.\\n\

//等待互斥信号,保证对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\// 等待资源

EnterCriticalSection (&RP_Write) ; // 写文件

printf(\"Writer thread %d begins to write to the file.\\n\Sleep(m_persist) ;

// 退出线程

printf(\"Writer thread %d finished writing to the file.\\n\//释放资源

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 ; // 初始化readcountt 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; 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\"); }

/////////////////////////////////////////////////////////////////// //主函数

int main(int argc, char* argv[]) { char ch;

while ( true ) {

printf(\"***********************************\\n\"); printf(\" 1: Reader Priority\\n\") ; printf(\" 2: Exit to Windows\\n\") ;

printf(\"***********************************\\n\"); printf( \"Enter your choice (1 or 2): \"); //如果输入信息不正确,继续输入 do{

ch = (char)_getch() ;

} while(ch != '1' && ch != '2');

system(\"cls\") ; //选择2,返回 if(ch == '2') return 0; //选择l,读者优先 else

ReaderPriority(\"thread.dat\"); //结束

printf(\"\\nPress Any Key To Continue:\"); _getch() ; system(\"cls\") ; } return 0; }

步骤3:编译并建立2-2.exe可执行文件。在工具栏单击“Execute Program”按钮,执行2-2.exe程序。

(1) 对运行的结果逐行给出分析描述。 Reader Priority:

Writer thread 1 sents the writing require. Writer thread 1 begins to write to the file. Writer thread 2 sents the writing require. Writer thread 2 sents the writing require. Reader thread 3 sents the reading require...... Writer thread 1 finished writing to the file. Writer thread 2 begins to write to the file. Writer thread 2 finished writing to the file. Reader thread 3 begins to read file. Reader thread 3 finished reading file. All reader and writer have finished operating.

Press Any Key To Continue:

(2) 根据运行输出结果,对照分析2-2程序,画出程序运行的流程图

(3) 自己定义一组实验数据并保存到文件后再执行程序(文件名字为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 to the file. Writer thread 2 finished writing to the file. Writer thread 5 begins to write to the file. Writer thread 5 finished writing to the file. All reader and writer have finished operating.

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务