操作系统实验二并发与调度
操作系统实验报告进程调度

五邑大学实验报告操作系统课程2016~2017年度第1学期实验题目:进程调度院系:计算机学院班级: 140801学号: 3114002472姓名:黄凯鑫任课教师:白明成绩评定:实验二题目:进程调度完成日期:2016年12 月11 日1、实验目的(1)设计一个有n个进程工行的进程调度程序。
每个进程由一个进程控制块(PCB)表示。
进程控制块通常应包含下述信息:进程名、进程优先数、进程需要运行的时间、占用CPU的时间以及进程的状态等,且可按调度算法的不同而增删。
(2)调度程序应包含2~3种不同的调度算法,运行时可任意选一种,以利于各种算法的分析比较。
(3)系统应能显示或打印各进程状态和参数的变化情况,便于观察诸进程的调度过程2、实验内容(1)编制和调试示例给出的进程调度程序,并使其投入运行。
(2)自行设计或改写一个进程调度程序,在相应机器上调试和运行该程序,其功能应该不亚于示例。
(3)直观地评测各种调度算法的性能。
3、算法设计算法:(1) 优先数法。
进程就绪链按优先数大小从高到低排列,链首进程首先投入运行。
每过一个时间片,运行进程所需运行的时间片数减1,说明它已运行了一个时间片,优先数也减3,理由是该进程如果在一个时间片中完成不了,优先级应该降低一级。
接着比较现行进程和就绪链链首进程的优先数,如果仍是现行进程高或者相同,就让现行进程继续进行,否则,调度就绪链链首进程投入运行。
原运行进程再按其优先数大小插入就绪链,且改变它们对应的进程状态,直至所有进程都运行完各自的时间片数。
(2) 简单轮转法。
进程就绪链按各进程进入的先后次序排列,进程每次占用处理机的轮转时间按其重要程度登入进程控制块中的轮转时间片数记录项(相当于优先数法的优先数记录项位置)。
每过一个时间片,运行进程占用处理机的时间片数加1,然后比较占用处理机的时间片数是否与该进程的轮转时间片数相等,若相等说明已到达轮转时间,应将现运行进程排到就绪链末尾,调度链首进程占用处理机,且改变它们的进程状态,直至所有进程完成各自的时间片。
操作系统实验二实验报告

操作系统实验二实验报告一、实验目的本次操作系统实验二的主要目的是深入理解和掌握进程管理的相关概念和技术,包括进程的创建、执行、同步和通信。
通过实际编程和实验操作,提高对操作系统原理的认识,培养解决实际问题的能力。
二、实验环境本次实验使用的操作系统为 Windows 10,编程环境为 Visual Studio 2019。
三、实验内容及步骤(一)进程创建实验1、首先,创建一个新的 C++项目。
2、在项目中,使用 Windows API 函数`CreateProcess`来创建一个新的进程。
3、为新进程指定可执行文件的路径、命令行参数、进程属性等。
4、编写代码来等待新进程的结束,并获取其退出代码。
(二)进程同步实验1、设计一个生产者消费者问题的模型。
2、使用信号量来实现生产者和消费者进程之间的同步。
3、生产者进程不断生成数据并放入共享缓冲区,当缓冲区已满时等待。
4、消费者进程从共享缓冲区中取出数据进行处理,当缓冲区为空时等待。
(三)进程通信实验1、选择使用管道来实现进程之间的通信。
2、创建一个匿名管道,父进程和子进程分别读写管道的两端。
3、父进程向管道写入数据,子进程从管道读取数据并进行处理。
四、实验结果及分析(一)进程创建实验结果成功创建了新的进程,并能够获取到其退出代码。
通过观察进程的创建和执行过程,加深了对进程概念的理解。
(二)进程同步实验结果通过使用信号量,生产者和消费者进程能够正确地进行同步,避免了缓冲区的溢出和数据的丢失。
分析结果表明,信号量机制有效地解决了进程之间的资源竞争和协调问题。
(三)进程通信实验结果通过管道实现了父进程和子进程之间的数据通信。
数据能够准确地在进程之间传递,验证了管道通信的有效性。
五、遇到的问题及解决方法(一)在进程创建实验中,遇到了参数设置不正确导致进程创建失败的问题。
通过仔细查阅文档和调试,最终正确设置了参数,成功创建了进程。
(二)在进程同步实验中,出现了信号量使用不当导致死锁的情况。
操作系统实验报告(进程调度)

华中师范大学计算机科学系《操作系统》实验报告实验题目:进程调度学生姓名:日期:2011-12-9实验2进程调度进程完成,撤消该进程就绪队列首进程投入运行 时间片到,运行进程已占用CPU 时间+1 运行进程已占用CPU 时间已达到所需的运行时间 把运行进程插入到下一个队列的队尾插入新的进程开始 初始化PCB,输入进程信息 所有队列都为空 各进程按FCFS 原则排队等待调度 【实验目的】(1)通过编写程序实现进程或作业先来先服务、高优先权、按时间片轮转调度算法,使学生进一步掌握进程调度的概念和算法,加深对处理机分配的理解。
(2)了解Windows2000/XP 中进程(线程)的调度机制。
(3)学习使用Windows2000/XP 中进程(线程)调度算法,掌握相应的与调度有关的Win32 API 函数。
【实验内容】在Windows XP 、Windows 2000等操作系统下,使用的VC 、VB 、java 或C 等编程语言,利用相应的WIN32 API 函数,编写程序实现进程或作业先来先服务、高优先权、按时间片轮转调度算法。
【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)1、进程调度算法:采用多级反馈队列调度算法。
其基本思想是:当一个新进程进入内在后,首先将它放入第一个队列的末尾,按FCFS 原则排队等待高度。
当轮到该进程执行时,如能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚为完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS 原则等待调度执行,以此类推。
2、实验步骤:(1)按先来先服务算法将进程排成就绪队列。
(2)检查所有队列是否为空,若空则退出,否则将队首进程调入执行。
(3)检查该运行进程是否运行完毕,若运行完毕,则撤消进程,否则,将该进程插入到下一个逻辑队列的队尾。
(4)是否再插入新的进程,若是则把它放到第一逻辑队列的列尾。
(5)重复步骤(2)、(3)、(4),直到就绪队列为空。
操作系统实验二进程调度

实验二进程调度算法的设计一、实验内容实现短进程优先调度算法(SPF)和时间片轮转调度算法(RR)二、实验目的通过对进程调度算法的设计,深入理解进程调度的原理。
进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
进程调度分配处理机,是控制协调进程对CPU的竞争,即按一定的调度算法从就绪队列中选中一个进程,把CPU的使用权交给被选中的进程。
进程通过定义一个进程控制块的数据结构(PCB)来表示;每个进程需要赋予进程ID、进程到达时间、进程需要运行的总时间的属性;在RR中,以1为时间片单位;运行时,输入若干个进程序列,按照时间片输出其执行序列。
三、实验题目实现短进程优先调度算法(SPF)和时间片轮转调度算法(RR)[提示]:(1) 先来先服务(FCFS)调度算法原理:每次调度是从就绪队列中,选择一个最先进入就绪队列的进程,把处理器分配给该进程,使之得到执行。
该进程一旦占有了处理器,它就一直运行下去,直到该进程完成或因发生事件而阻塞,才退出处理器。
将用户作业和就绪进程按提交顺序或变为就绪状态的先后排成队列,并按照先来先服务的方式进行调度处理,是一种最普遍和最简单的方法。
它优先考虑在系统中等待时间最长的作业,而不管要求运行时间的长短。
按照就绪进程进入就绪队列的先后次序进行调度,简单易实现,利于长进程,CPU繁忙型作业,不利于短进程,排队时间相对过长。
(2) 时间片轮转调度算法RR原理:时间片轮转法主要用于进程调度。
采用此算法的系统,其程序就绪队列往往按进程到达的时间来排序。
进程调度按一定时间片(q)轮番运行各个进程.进程按到达时间在就绪队列中排队,调度程序每次把CPU分配给就绪队列首进程使用一个时间片,运行完一个时间片释放CPU,排到就绪队列末尾参加下一轮调度,CPU分配给就绪队列的首进程。
固定时间片轮转法:1 所有就绪进程按 FCFS 规则排队。
2 处理机总是分配给就绪队列的队首进程。
操作系统实验二并发与调度

实验二并发与调度一、实验目的在本实验中,通过对事件和互斥体对象的了解,来加深对Windows 2000线程同步的理解。
通过分析实验程序,了解管理事件对象的API。
了解在进程中如何使用事件对象,在进程中如何使用互斥体对象,线程如何通过文件映射对象发送数据。
二、实验环境硬件环境:计算机一台,局域网环境;软件环境:Windows 2000 Professional,Visual C++ 6.0专业版或企业版。
三、实验内容和步骤第一部分:互斥体对象本程序中显示的类CCountUpDown使用了一个互斥体来保证对两个线程间单一数值的访问。
每个线程都企图获得控制权来改变该数值,然后将该数值写入输出流中。
创建者实际上创建的是互斥体对象,计数方法执行等待并释放,为的是共同使用互斥体所需的资源(因而也就是共享资源) 。
1、利用互斥体保护共享资源程序参见实验指导书分析程序的运行结果,可以看到线程(加和减线程) 的交替执行(因为Sleep() API允许Windows切换线程) 。
在每次运行之后,数值应该返回初始值(0) ,因为在每次运行之后写入线程在等待队列中变成最后一个,内核保证它在其他线程工作时不会再运行。
1)请描述运行结果(如果运行不成功,则可能的原因是什么?) :2) 根据运行输出结果,对照分析程序,可以看出程序运行的流程吗?请简单描述:_____逆向运行__________第二部分线程通过文件对象发送数据Windows 2000提供的线程间通讯类内核对象允许同一进程或跨进程的线程之间互相发送信息,包括文件、文件映射、邮件位和命名管道等,其中最常用的是文件和文件映射。
这类对象允许一个线程很容易地向同一进程或其他进程中的另一线程发送信息。
1、演示线程通过文件对象发送数据程序参见实验指导书运行结果(如果运行不成功,则可能的原因是什么?) :阅读和分析程序,请回答问题:1) 程序中启动了多少个单独的读写线程?__________100__________________________________________________________2) 使用了哪个系统API函数来创建线程例程?_________ CreateThread()________________________________3) 文件的读和写操作分别使用了哪个API函数?_______ ReadFile()______ WriteFile()_____________ 每次运行进程时,都可看到程序中的每个线程从前面的线程中读取数据并将数据增加,文件中的数值连续增加。
操作系统实验报告作业调度

操作系统实验报告作业调度操作系统实验报告:作业调度引言作业调度是操作系统中的重要部分,它负责管理和调度系统中的各种作业,以最大化系统资源的利用率和提高作业的执行效率。
在本次实验中,我们将探讨作业调度的基本原理和实现方法,并通过实验验证其效果。
实验目的本次实验的主要目的是通过实际操作,了解作业调度的基本原理和实现方法,掌握作业调度的相关算法,并通过实验验证其有效性。
实验内容1. 实现作业调度的基本算法在本次实验中,我们将实现作业调度的基本算法,包括先来先服务(FCFS)、最短作业优先(SJF)、优先级调度(Priority Scheduling)和多级反馈队列调度(Multilevel Feedback Queue Scheduling)等。
通过编写代码,模拟这些算法的执行过程,并观察它们的效果。
2. 实验验证我们将设计一些测试用例,通过模拟作业的执行过程,分别使用不同的作业调度算法,并比较它们的执行效果。
通过实验验证,我们将得出不同算法的优劣势,并分析其适用场景。
实验结果经过实验验证,我们得出以下结论:1. 先来先服务(FCFS)算法适用于作业执行时间相对均匀的情况,但可能会导致平均等待时间较长。
2. 最短作业优先(SJF)算法能够最大程度地减少平均等待时间,但可能会出现作业饥饿现象。
3. 优先级调度(Priority Scheduling)算法能够根据作业的优先级进行调度,适用于有明确优先级需求的情况。
4. 多级反馈队列调度(Multilevel Feedback Queue Scheduling)算法能够根据作业的执行情况动态调整优先级,适用于各种类型的作业。
结论作业调度是操作系统中的重要组成部分,不同的作业调度算法适用于不同的场景。
通过本次实验,我们深入了解了作业调度的基本原理和实现方法,掌握了不同算法的优劣势,并通过实验验证了它们的有效性。
这将对我们进一步深入学习操作系统和提高系统性能有着重要的意义。
电子科技大学《计算机操作系统》第2章 并发与进程-调度共73页

2.5.2 调度的类型
• 同时具有三级调度的调度队列模型
长程调 度
时间片用完
就绪队列
处理机完成ຫໍສະໝຸດ 就绪/挂起队列短程调度
阻塞/挂起队列
阻塞队列 事件发生
中程调度 事件等待
2.5.3 进程调度算法
• 调度算法
– 根据系统的资源分配策略所规定的资源分配算法 – 对于不同的系统目标,通常采用不同的调度算法
进程名 产生时间 服务时间 优先级 时间片
P1
0
2
2
P2
1
6
1
1
P3
2
1
4
P4
3
5
3
2.5.3.1 先来先服务(FCFS)
进程名 产生时间
P1
0
P2
1
P3
2
P4
3
服务时间 2 6 1 5
优先级 2 1 4 3
时间片 1
P2 P1
P4 P3
2 4 6 8 10 12 14 16
t
P1 P2 P3 P4
当一个新进程加入就绪队列时,它可能比当前运行 的进程具有更短的剩余时间。只要新进程就绪,调度程 序就可能抢占当前正在运行的进程。
• 优点
– 既不像FCFS那样偏爱长进程,也不像RR算法那样会产 生额外的中断,从而减少了开销。
– 周转时间方面,SRT比SJF性能要好,短作业可以立即 被选择执行。
2.5.3.5 剩余时间最短者优先算法
在创建进程时所赋予的优先级可随进程的推进或随 其等待时间的增加而改变,以便获得更好的调度性能。
• 调整时机
– 时钟中断 – 进程切换 – 进程终止
操作系统实验二进程调度

实验二:进程调度一、目的要求:用C或C++语言编写和调试一个进程调度程序,以加深对进程的概念及进程调度算法的理解.二、进程调度算法:采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法。
每个进程有一个进程控制块( PCB)表示。
进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。
进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生)。
进程的到达时间为进程输入的时间。
进程的运行时间以时间片为单位进行计算。
每个进程的状态可以是就绪 W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。
就绪进程获得 CPU后都只能运行一个时间片。
用已占用CPU时间加1来表示。
如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。
每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。
重复以上过程,直到所要进程都完成为止。
三调度算法的流程图如下 :四、程序代码:#include<iostream>using namespace std;#define MAX 10struct task_struct{char name[10]; /*进程名称*/ int number; /*进程编号*/ float come_time; /*到达时间*/float run_begin_time; /*开始运行时间*/ float run_time; /*运行时间*/float run_end_time; /*运行结束时间*/ int priority; /*优先级*/int order; /*运行次序*/int run_flag; /*调度标志*/}tasks[MAX];int counter; /*实际进程个数*/int fcfs(); /*先来先服务*/int ps(); /*优先级调度*/int sjf(); /*短作业优先*/int hrrn(); /*响应比高优先*/int pinput(); /*进程参数输入*/int poutput(); /*调度结果输出*/void main(){int option;pinput();printf("请选择调度算法(0~4):\n");printf("1.先来先服务\n");printf("2.优先级调度\n");printf(" 3.短作业优先\n");printf(" 4.响应比高优先\n");printf(" 0.退出\n");scanf("%d",&option);switch (option){ case 0:printf("运行结束。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
步骤3:在Visual Studio 2019窗口的工具栏中单击“文件”按钮,在解决方案OSLab中新建一个项目,命名为Lab2_1,并在源文件中添加新建项,命名为Lab2_1.cpp,将实验指导书中的代码拷贝至该文件中。
步骤4:单击“生成”菜单中的“编译”命令,系统对Lab2_1.cpp进行编译。
步骤5:编译完成后,单击“生成”菜单中的“生成”命令,建立Lab2_1.exe可执行文件。
操作能否正常进行?如果不行,则可能的原因是什么?由于程序代码是由pdf文件复制出来的,调整缩进与分号等规范后,进一步调整了安全性,如使用sprint_s等,并使用了命名空间std,减少代码冗余,调整过后,程序代码可以调通。
步骤6:在解决方案资源管理器中右键当前项目,并设置为启动项目。
在工具栏单击“调试”-“开始执行(不调试)”按钮,执行Lab2_1.exe程序。
运行结果(分行书写。
如果运行不成功,则可能的原因是什么?):图2-1 程序Lab2_1运行结果1)event created2)child created3)Parent waiting on child.4)child process begining......5)event signaled6)parent received the event signaling from child7)Parent released.这个结果与你期望的一致吗?(从进程并发的角度对结果进行分析)结果与期望一致。
图2-3 程序Lab3_2运行结果2)根据运行输出结果,对照分析Lab2_2程序,可以看出程序运行的流程吗?请简单描述:该程序使用了互斥体CCountUpDown来保护能被同时访问的共享资源。
该程序首先创建了两个进程来访问共享值,并创建了互斥体来访问数值,随后两个进程依次轮流访问数值(访问后改变并释放)。
五、实验结果与分析实验结果与分析见实验步骤与实验过程。
六、实验心得体会通过本次实验,我加深了对计算机线程的理解,并深入理解了互斥体等相关概念。
通过分析实验程序,了解管理事件对象的API;通过对事件和互斥体对象的编程,加深了对 Windows Server 2016 线程同步的理解。
附录程序清单清单2-11.// event 项目2.#include <windows.h>3.#include <iostream>ing namespace std;5.6.// 以下是句柄事件。
实际中很可能使用共享的包含文件来进行通讯7.static LPCTSTR g_szContinueEvent = "w2kdg.EventDemo.event.Continue";8.9.// 本方法只是创建了一个进程的副本,以子进程模式 (由命令行指定) 工作10.BOOL CreateChild()11.{12.// 提取当前可执行文件的文件名13.TCHAR szFilename[MAX_PATH];14. ::GetModuleFileName(NULL, szFilename, MAX_PATH);15.16.// 格式化用于子进程的命令行,指明它是一个EXE文件和子进程17.TCHAR szCmdLine[MAX_PATH];18. ::sprintf_s(szCmdLine, "\"%s\" child", szFilename);19.// 子进程的启动信息结构20. STARTUPINFO si;21. ::ZeroMemory(reinterpret_cast<void*>(&si), sizeof(si));22. si.cb = sizeof(si); // 必须是本结构的大小23.// 返回的子进程的进程信息结构24. PROCESS_INFORMATION pi;25.// 使用同一可执行文件和告诉它是一个子进程的命令行创建进程26.BOOL bCreateOK = ::CreateProcess(27. szFilename, // 生成的可执行文件名28. szCmdLine, // 指示其行为与子进程一样的标志29. NULL, // 子进程句柄的安全性30. NULL, // 子线程句柄的安全性31. FALSE, // 不继承句柄32. 0, // 特殊的创建标志33. NULL, // 新环境34. NULL, // 当前目录35. &si, // 启动信息结构36. &pi); // 返回的进程信息结构37.38.// 释放对子进程的引用39.if (bCreateOK) {40. ::CloseHandle(pi.hProcess);41. ::CloseHandle(pi.hThread);42. }43.return (bCreateOK);44.}45.46.// 下面的方法创建一个事件和一个子进程,然后等待子进程在返回前向事件发出信号47.void WaitForChild()48.{49.// create a new event object for the child process50.// to use when releasing this process51.HANDLE hEventContinue = ::CreateEvent(52. NULL, // 缺省的安全性,子进程将具有访问权限53. TRUE, // 手工重置事件54. FALSE, // 初始时是非接受信号状态55. g_szContinueEvent); // 事件名称56.57.if (hEventContinue != NULL)58. {59. cout << "event created " << endl;60.// 创建子进程61.if (::CreateChild())62. {63. cout << "child created" << endl;64.// 等待,直到子进程发出信号65. cout << "Parent waiting on child." << endl;66. ::WaitForSingleObject(hEventContinue, INFINITE);67. ::Sleep(1500); // 删去这句试试68. cout << "parent received the event signaling from child" << endl;69. }70.71.// 清除句柄72. ::CloseHandle(hEventContinue);73. hEventContinue = INVALID_HANDLE_VALUE;74. }75.}76.77.// 以下方法在子进程模式下被调用,其功能只是向父进程发出终止信号78.void SignalParent()79.{80.// 尝试打开句柄81. cout << "child process begining......" << endl;82.HANDLE hEventContinue = ::OpenEvent(83. EVENT_MODIFY_STATE, // 所要求的最小访问权限84. FALSE, // 不是可继承的句柄85. g_szContinueEvent); // 事件名称86.87.if (hEventContinue != NULL)88. {89. ::SetEvent(hEventContinue);90. cout << "event signaled" << endl;91. }92.93.// 清除句柄94. ::CloseHandle(hEventContinue);95. hEventContinue = INVALID_HANDLE_VALUE;96.}97.98.int main(int argc, char* argv[])99.{100.// 检查父进程或是子进程是否启动101.if (argc > 1 && ::strcmp(argv[1], "child") == 0)102. {103.// 向父进程创建的事件发出信号104. ::SignalParent();105. }else{106.// 创建一个事件并等待子进程发出信号107. ::WaitForChild();108. ::Sleep(1500);109. cout << "Parent released." << endl;110. }111.return 0;112.}清单2-21.// mutex 项目2.#include <windows.h>3.#include <iostream>ing namespace std;5.6.// 利用互斥体来保护同时访问的共享资源7.class CCountUpDown8.{9.public:10.// 创建者创建两个线程来访问共享值11. CCountUpDown(int nAccesses) : m_hThreadInc(INVALID_HANDLE_VALUE), m_hThreadDec(INVALID_HANDLE_VALUE),12. m_hMutexValue(INVALID_HANDLE_VALUE), m_nValue(0), m_nAccess(nAccesses)13. {14.// 创建互斥体用于访问数值15. m_hMutexValue = ::CreateMutex(16. NULL, // 缺省的安全性17. TRUE, // 初始时拥有,在所有的初始化结束时将释放18. NULL); // 匿名的19.20. m_hThreadInc = ::CreateThread(21. NULL, // 缺省的安全性22. 0, // 缺省堆栈23. IncThreadProc, // 类线程进程24.reinterpret_cast<LPVOID>(this), // 线程参数25. 0, // 无特殊的标志26. NULL); // 忽略返回的 id27.28. m_hThreadDec = ::CreateThread(29. NULL, // 缺省的安全性30. 0, // 缺省堆栈31. DecThreadProc, // 类线程进程32.reinterpret_cast<LPVOID>(this), // 线程参数33. 0, // 无特殊的标志34. NULL); // 忽略返回的 id35.36.// 允许另一线程获得互斥体37. ::ReleaseMutex(m_hMutexValue);38. }39.40.// 解除程序释放对对象的引用41.virtual ~CCountUpDown()42. {43. ::CloseHandle(m_hThreadInc);44. ::CloseHandle(m_hThreadDec);45. ::CloseHandle(m_hMutexValue);46. }47.48.// 简单的等待方法,在两个线程终止之前可暂停主调者49.virtual void WaitForCompletion()50. {51.// 确保所有对象都已准备好52.if (m_hThreadInc != INVALID_HANDLE_VALUE &&53. m_hThreadDec != INVALID_HANDLE_VALUE)54. {55.// 等待两者完成 (顺序并不重要)56. ::WaitForSingleObject(m_hThreadInc, INFINITE);57. ::WaitForSingleObject(m_hThreadDec, INFINITE);58. }59. }60.61.protected:62.63.// 改变共享资源的简单的方法64.virtual void DoCount(int nStep)65. {66.// 循环,直到所有的访问都结束为止67.while (m_nAccess > 0)68. {69.// 等待访问数值70. ::WaitForSingleObject(m_hMutexValue, INFINITE);71.// 改变并显示该值72. m_nValue += nStep;73. cout << "thread: " << ::GetCurrentThreadId()74. << " value: " << m_nValue75. << " access: " << m_nAccess << endl;76.// 发出访问信号并允许线程切换77. --m_nAccess;78. ::Sleep(1000); // 使显示速度放慢79.// 释放对数值的访问80. ::ReleaseMutex(m_hMutexValue);81. }82. }83.84.static DWORD WINAPI IncThreadProc(LPVOID lpParam)85. {86.// 将参数解释为 'this' 指针87. CCountUpDown* pThis =88.reinterpret_cast<CCountUpDown*>(lpParam);89.// 调用对象的增加方法并返回一个值90. pThis->DoCount(+1);91.return (0);92. }93.94.static DWORD WINAPI DecThreadProc(LPVOID lpParam)95. {96.// 将参数解释为 'this' 指针97. CCountUpDown* pThis =98.reinterpret_cast<CCountUpDown*>(lpParam);99.// 调用对象的减少方法并返回一个值100. pThis->DoCount(-1);101.return (0);102. }103.104.p rotected:105.HANDLE m_hThreadInc;106.HANDLE m_hThreadDec;107.HANDLE m_hMutexValue;108.int m_nValue;109.int m_nAccess;110.};。