《11程序设计实践6w-线程、模块化、时间函数和设计问题.pptx》由会员分享,可在线阅读,更多相关《11程序设计实践6w-线程、模块化、时间函数和设计问题.pptx(77页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、 模块化设计问题模块化设计问题1 提纲提纲1.使用线程实现任务并发使用线程实现任务并发2.模块化和工程模块化和工程3.概要设计要点概要设计要点4.时间控制函数时间控制函数5.有限状态自动机解题有限状态自动机解题2 1.使用线程实现任务并发问题的引出:以电梯控制系统为例问题的引出:以电梯控制系统为例目前能想到的程序主体结构目前能想到的程序主体结构main()while(1)state_trans();/计算此刻电梯的状态计算此刻电梯的状态print_message();/输出电梯此刻的状态输出电梯此刻的状态,包括动画包括动画get_input();/接收当前时刻的新输入(包括新目接收当前时刻的新
2、输入(包括新目标和新呼叫)标和新呼叫)control();/*根据控制策略确定下一目标楼层,根据控制策略确定下一目标楼层,在在state_trans()中要用到中要用到*/time_count();/时间片推进一个时间片推进一个思考:上述结构不合理之处?思考:上述结构不合理之处?3 1.使用线程实现任务并发上述结构不合理之处上述结构不合理之处:计算和输出电梯状态与接收服务计算和输出电梯状态与接收服务请求是串行的,与现实中的电梯运行不符!请求是串行的,与现实中的电梯运行不符!程序结构的改进:程序结构的改进:从上述代码中删除从上述代码中删除get_input(),从而实现每隔一小段时,从而实现每隔
3、一小段时间就刷新电梯当前状态间就刷新电梯当前状态main()while(1)state_trans();/计算此刻电梯的状态计算此刻电梯的状态print_message();/输出电梯此刻的状态输出电梯此刻的状态control();/*根据控制策略确定下一目标楼层,这根据控制策略确定下一目标楼层,这在在state_trans()中要用到中要用到*/time_count();/时间片推进一个时间片推进一个4 1.使用线程实现任务并发但是,程序必须要能接收电梯服务请求,如何但是,程序必须要能接收电梯服务请求,如何处理服务请求的输入?处理服务请求的输入?理想状态:理想状态:电梯服务请求的接收和电梯状
4、态的计算输出电梯服务请求的接收和电梯状态的计算输出能同时进行,互不影响能同时进行,互不影响但是,能否实现?但是,能否实现?答案是:使用线程答案是:使用线程电梯状电梯状态计算态计算和输出和输出共享内存区共享内存区接收服接收服务请求务请求5 1.使用线程实现任务并发进程进程一个正在运行的程序的实例,是一个程序在其自身一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动,例如的地址空间中的一次执行活动,例如n用字处理软件编辑文稿时,同时打开用字处理软件编辑文稿时,同时打开mp3播放程序听播放程序听音乐,这两个独立的程序在同时运行,称为两个进程音乐,这两个独立的程序在同时运行,称为两
5、个进程进程是资源申请、调度和独立运行的单位进程是资源申请、调度和独立运行的单位6 1.使用线程实现任务并发线程线程线程是系统分配处理器时间资源的基本单元。对于操作系统线程是系统分配处理器时间资源的基本单元。对于操作系统而言,其调度单元是线程(而言,其调度单元是线程(为线程提供时间片,线程在自己为线程提供时间片,线程在自己的时间片内运行)的时间片内运行)。一个程序中多段代码同时并发执行,称为多线程一个程序中多段代码同时并发执行,称为多线程n譬如用譬如用word同时打开多个文档进行编辑,用同时打开多个文档进行编辑,用IE浏览浏览器同时访问多个网站器同时访问多个网站通过多线程,一个通过多线程,一个进
6、程进程表面上看同时可以执行一个以上表面上看同时可以执行一个以上的任务的任务并发并发7 线程(续)线程(续)一个进程至少包括一个线程(称为主线程)。一个进程至少包括一个线程(称为主线程)。一个进程从主线程的执行开始进而创建一个一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程的多或多个附加线程,就是所谓基于多线程的多任务。任务。线程自己不拥有系统资源,但它可与同属一线程自己不拥有系统资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资个进程的其它线程共享进程所拥有的全部资源源8 1.使用线程实现任务并发在在C程序中要创建线程,可以调用程序中要创建线程,可以调用Wind
7、ows操作系统提供的创操作系统提供的创建线程的函数建线程的函数CreateThread:HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,LPDWORDlpThreadId);LPVOID是一个是一个Void类型的指针,也就是说你可以将任意类类型的指针,也就是说你可以将任意类型的指针赋值给型的指针赋值给LPVOID类型的变量。类型的变量。DWO
8、RD是是32位无符号整数。位无符号整数。9 1.使用线程实现任务并发lpThreadAttributes表示创建线程的安全属性,表示创建线程的安全属性,NT下有用。可赋值下有用。可赋值为为NULL。dwStackSize指定线程栈的尺寸,如果为指定线程栈的尺寸,如果为0则与进程主线程栈相同。则与进程主线程栈相同。lpStartAddress指定线程开始运行的地址。赋值为指向函数的指针,指定线程开始运行的地址。赋值为指向函数的指针,即函数名。该函数的名称任意,但函数类型必须遵照下述声明形式即函数名。该函数的名称任意,但函数类型必须遵照下述声明形式:DWORDWINAPIThreadProc(LP
9、VOIDlpParameter);否则需要进否则需要进行强制类型转换行强制类型转换lpParameter表示传递给线程的表示传递给线程的32位的参数(数值或指针)。位的参数(数值或指针)。若无若无参数则赋值为参数则赋值为NULL。dwCreationFlags表示是否创建后挂起线程表示是否创建后挂起线程(取值取值CREATE_SUSPENDED表示挂起,取值表示挂起,取值0表示创建后立即运行表示创建后立即运行),挂起后调用挂起后调用ResumeThread继续执行。若不挂起则赋值为继续执行。若不挂起则赋值为0。lpThreadId用来存放返回的线程用来存放返回的线程ID。DWORDThread
10、ID1=1;HANDLEhRead1=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)getInput,NULL,0,&ThreadID1);10#include#includeDWORDWINAPIFun1Proc(LPVOIDlpParameter);intmain()HANDLEhThreadl;/hThreadl=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);/CloseHandle(hThreadl);/printf(mainthreadisrunningn);/return0;11DWORDWINAPIF
11、un1Proc(LPVOIDlpParameter)printf(hThreadlisrunningn);return0;例例1 12#include#includeDWORDWINAPIFun1Proc(LPVOIDlpParameter)intmain()HANDLEhThreadl;/hThreadl=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);/CloseHandle(hThreadl);/printf(mainthreadisrunningn);/Sleep(10);/让线程睡眠让线程睡眠10毫秒毫秒return0;例例2#include#in
12、cludeintindex=0;DWORDWINAPIFun1Proc(LPVOIDlpParameter);intmain()HANDLEhThreadl;/hThreadl=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);/CloseHandle(hThreadl);/while(index+1000)printf(mainthreadisrunningn);/return0;13例例3DWORDWINAPIFun1Proc(LPVOIDlpParameter)while(index+1000)printf(hThreadlisrunningn);ret
13、urn0;14 15#include#includeinttickets=100;DWORDWINAPIFun1Proc(LPVOIDpPararneter)while(tickets0)printf(“thread1sellticket:%dn”,tickets-);return0;DWORDWINAPIFun2Proc(LPVOIDpPararneter)while(tickets0)printf(“thread2sellticket:%dn”,tickets-);return0;intmain()HANDLEhThread1=CreateThread(NULL,0,Fun1Proc,NU
14、LL,0,NULL);HANDLEhThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);Sleep(4000);return0;16 17 线程的同步线程的同步利用互斥对象利用互斥对象(mutex)实现线程的同步,互斥对象能实现线程的同步,互斥对象能够确保线程拥有对单个资源的互斥访问权。够确保线程拥有对单个资源的互斥访问权。3个操作个操作n互斥对象的创建互斥对象的创建n互斥对象的释放互斥对象的释放n互斥对象的请求互斥对象的请求18 互斥对象的创建HANDLE
15、CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL binitialOwner,LPCTSTR lpNarne)nlpMutexAttributes:可以给该参数传递 NULL值,让互斥对象使用默认的安全性nbinitialOwner:BOOL类型,指定互斥对象初始的拥有者。如果该值为真,则创建这个互斥对象的线程获得该对象的所有权;否则,该线程将不获得所创建的互斥对象的所有权。nlpName:指定互斥对象的名称。如果此参数为 NULL.则创建一个匿名的互斥对象。如果调用成功,该函数将返回所创建的互斥对象的句柄19 互斥对象的释放BO
16、OL ReleaseMutex(HANDLE hMutex);ReleaseMutex函数只有一个HANDLE类型的参数,即需要释放的互斥对象的句柄。该函数的返回值是BOOL类型,如果函数调用成功,返回非0值;否则返回0值。20 互斥对象的请求DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);Handle:所请求的互斥对象的句柄。一旦互斥对象处于有信号状态,则该函数就返回。如果该互斥对象始终处于无信号状态,即未通知的状态,则该函数就会一直等待,这样就会暂停线程的执行。dwMilliseconds:指定等待的时间间隔,以
17、毫秒为单位。如果指定的时间间隔己过,即使所请求的对象仍处于无信号状态,WaitForSingleObject函数也会返回。如果将此参数设置为0,那么 WaitForSingleObject函数将测试该对象的状态并立即返回;如果将此参数设置为INFINITE,则该函数会永远等待,直到等待的对象处于有信号状态才会返回。调用WaitForSingleObject函数后,该函数会一直等待,只有在以下两种情况下才会返回:1)指定的对象变成有信号状态。2)指定的等待时间间隔己过。21 HANDLEhMutex;inttickets=100;DWORDWINAPIFun1Proc(LPVOIDpPararn
18、eter)while(tickets0)WaitForSingleObject(hMutex,INFINITE);if(tickets0)printf(thread1sellticket:%dn,tickets-);ReleaseMutex(hMutex);return0;22 DWORDWINAPIFun2Proc(LPVOIDpPararneter)while(tickets0)WaitForSingleObject(hMutex,INFINITE);if(tickets0)printf(thread2sellticket:%dn,tickets-);ReleaseMutex(hMutex
19、);return0;23 intmain()HANDLEhThread1,hThread2;hMutex=CreateMutex(NULL,FALSE,NULL);hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);Sleep(4000);return0;24 线程在电梯控制系统中的使用线程在电梯控制系统中的使用考虑现实中安装在电梯考虑现实中安装在电梯上的软
20、件:接收电梯服上的软件:接收电梯服务请求和计算电梯状态、务请求和计算电梯状态、从而控制电梯的运行是从而控制电梯的运行是并行的并行的因此我们可以考虑在模因此我们可以考虑在模拟电梯控制系统中设计拟电梯控制系统中设计一个线程专门用于接收一个线程专门用于接收电梯服务请求,另一个电梯服务请求,另一个线程实行电梯的状态计线程实行电梯的状态计算和状态输出算和状态输出电梯状电梯状态计算态计算和输出和输出线程共享内存区线程共享内存区接收服接收服务请求务请求电梯状态计算和输出电梯状态计算和输出:从共享内存:从共享内存区读取电梯请求,计算下一目标楼区读取电梯请求,计算下一目标楼层,从而确定电梯的下一状态。层,从而确
21、定电梯的下一状态。接收服务请求接收服务请求:接收电梯请求,将:接收电梯请求,将请求保存到内存。请求保存到内存。25 /主线程主线程main()DWORDThreadID;/创建线程(对应于函数创建线程(对应于函数input(),用于接收电梯输入;,用于接收电梯输入;HANDLEhRead=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)getInput,NULL,0,&ThreadID);/进行变量初始化工作进行变量初始化工作线程在电梯控制系统中的使用线程在电梯控制系统中的使用26 while(1)state_trans();();/根据自动机模型决定此
22、刻电梯的状态根据自动机模型决定此刻电梯的状态print_message();/*输出电梯此刻的状态输出电梯此刻的状态*/control();/*根据控制策略确定下一目标楼层,这在根据控制策略确定下一目标楼层,这在state_trans()中要用到中要用到*/time_count();/*时间片推进一个时间片推进一个*/线程在电梯控制系统中的使用线程在电梯控制系统中的使用27 /接收输入线程接收输入线程voidgetInput(void)charch;while(1)ch=getchar();/加入代码:将加入代码:将ch翻译成相应请求并保存;翻译成相应请求并保存;time_count();/时
23、间片推进一个;时间片推进一个;线程在电梯控制系统中的使用线程在电梯控制系统中的使用28 提纲提纲1.使用线程实现任务并发使用线程实现任务并发2.软件设计和模块化软件设计和模块化3.概要设计要点概要设计要点4.时间控制函数时间控制函数5.有限状态自动机解题有限状态自动机解题29 6.1 软件设计概述软件设计在开发阶段中的重要性软件设计在开发阶段中的重要性需求分析模型中的每一个成份都提供了建立需求分析模型中的每一个成份都提供了建立设计模型所需的信息。设计模型所需的信息。根据数据、功能和行为模型来表示的软件需根据数据、功能和行为模型来表示的软件需求,采用某种设计方法进行数据设计、系统求,采用某种设计
24、方法进行数据设计、系统结构设计和过程设计。结构设计和过程设计。软件需求分析:软件需求分析:(1)(1)问题的信息域必须被表示和理解。问题的信息域必须被表示和理解。(数据模型数据模型)(2)(2)软件将完成的功能必须被定义。软件将完成的功能必须被定义。(功能模型功能模型)(3)(3)软件的行为软件的行为(作为外部事件的结果作为外部事件的结果)必须被表示。必须被表示。(行为模型行为模型)31 数据模型数据模型问题的信息域包含三个不同的数据和控制视图:问题的信息域包含三个不同的数据和控制视图:(1 1)信息内容和关系)信息内容和关系信息内容表示了个体数据和控制对象,它们信息内容表示了个体数据和控制对
25、象,它们可和其他的数据和控制对象关联。可和其他的数据和控制对象关联。(2 2)信息流)信息流信息流表示了数据和控制在系统中流动时变信息流表示了数据和控制在系统中流动时变化的方式。化的方式。(3 3)信息结构)信息结构信息结构表示了各种数据和控制项的内部组信息结构表示了各种数据和控制项的内部组织。织。功能模型功能模型对进入软件的信息和数据进行变换和处理的模对进入软件的信息和数据进行变换和处理的模块,它必须至少完成三个常见功能:输入、处块,它必须至少完成三个常见功能:输入、处理和输出。功能模型从顶层的语境层模型开始,理和输出。功能模型从顶层的语境层模型开始,经过一系列的细化迭代,越来越多的功能细节
26、经过一系列的细化迭代,越来越多的功能细节被发现,直至得到所有系统功能。被发现,直至得到所有系统功能。4.3 软件需求分析建模的原则和方法行为模型行为模型大多数软件对来自外界的事件做出反应,这种大多数软件对来自外界的事件做出反应,这种刺激反应特征形成了行为模型的基础。一个刺激反应特征形成了行为模型的基础。一个计算机程序总是处于某个计算机程序总是处于某个状态状态:一种外部可观:一种外部可观测的行为模式(如等待、运行),它仅当某事测的行为模式(如等待、运行),它仅当某事件发生时才被改变。件发生时才被改变。6.1 软件设计概述 分析模型转换为软件设计的映射关系分析模型转换为软件设计的映射关系 6.1
27、软件设计概述数据设计将实体数据设计将实体关系图中描述的对象和关关系图中描述的对象和关系,以及数据词典中描述的详细数据内容转系,以及数据词典中描述的详细数据内容转化为数据结构的定义。化为数据结构的定义。系统结构设计定义软件系统各主要成份之间系统结构设计定义软件系统各主要成份之间的关系。接口设计根据数据流图定义软件内的关系。接口设计根据数据流图定义软件内部各成份之间、软件与其它协同系统之间以部各成份之间、软件与其它协同系统之间以及软件与用户之间的交互机制。及软件与用户之间的交互机制。过程设计则是把结构成份转换成软件的过程过程设计则是把结构成份转换成软件的过程性描述。在编码阶段,根据这种过程性描述,
28、性描述。在编码阶段,根据这种过程性描述,生成源程序代码,最终通过测试得到完整有生成源程序代码,最终通过测试得到完整有效的软件。效的软件。6.1 软件设计概述软件设计是开发阶段中最重要的步骤,它是软件开发过软件设计是开发阶段中最重要的步骤,它是软件开发过程中质量得以保证的关键步骤。程中质量得以保证的关键步骤。软件设计又是将用户要求准确地转化成为最终的软件产软件设计又是将用户要求准确地转化成为最终的软件产品的唯一途径。品的唯一途径。软件设计是后续开发步骤及软件维护工作的基础。软件设计是后续开发步骤及软件维护工作的基础。软件设计对后期开发的质量影响软件设计对后期开发的质量影响 6.1 软件设计概述软
29、件设计的过程软件设计的过程从工程管理角度来看,软件设计分两步完成。从工程管理角度来看,软件设计分两步完成。n概要设计:将软件需求转化为数据结构和软概要设计:将软件需求转化为数据结构和软件的系统结构模块;决定每个模块的功能;件的系统结构模块;决定每个模块的功能;决定模块之间的调用关系,即模块间传递的决定模块之间的调用关系,即模块间传递的数据;决定模块的接口。数据;决定模块的接口。n详细设计:在概要设计基础上确定如何实现详细设计:在概要设计基础上确定如何实现各模块的内部细节,即对模块内部的算法和各模块的内部细节,即对模块内部的算法和数据结构进行设计,产生详细设计文档。在数据结构进行设计,产生详细设
30、计文档。在后续的编码阶段就可以完全按照详细设计的后续的编码阶段就可以完全按照详细设计的细节过程来映射到代码,最终实现整个系统细节过程来映射到代码,最终实现整个系统 2.模块化和工程模块化和工程将复杂问题分解为若干较小问题,然后再去求解,有将复杂问题分解为若干较小问题,然后再去求解,有助于控制问题的复杂性,利于问题的解决。助于控制问题的复杂性,利于问题的解决。模块化:把程序划分成独立命名且可独立访问的模块,模块化:把程序划分成独立命名且可独立访问的模块,每个模块完成一个子功能(解决一个子问题),所有每个模块完成一个子功能(解决一个子问题),所有模块集成起来构成的整体可完成用户的所有需求。高模块集
31、成起来构成的整体可完成用户的所有需求。高层模块从整体上把握问题层模块从整体上把握问题,隐蔽细节。低层模块解决细隐蔽细节。低层模块解决细节问题。节问题。模块化意义模块化意义n降低了系统的复杂性,使系统容易修改和重用;降低了系统的复杂性,使系统容易修改和重用;n推动系统各部分的并行开发,提高开发效率。推动系统各部分的并行开发,提高开发效率。复杂问题复杂问题较小问题较小问题分解分解39 2.模块化和工程模块化和工程模块的定义模块的定义一般把用一个名字就可调用的一段程序称为一般把用一个名字就可调用的一段程序称为“模块模块”,如子程序、函数等。函数是最小的模块,若干,如子程序、函数等。函数是最小的模块,
32、若干个紧密相关的函数可以组成更大的模块源文件。个紧密相关的函数可以组成更大的模块源文件。模块的基本属性:模块的基本属性:n功能功能:描述该模块实现什么功能:描述该模块实现什么功能n逻辑逻辑:描述模块内部怎么做:描述模块内部怎么做n状态状态:该模块使用时的环境和条件:该模块使用时的环境和条件40 2.模块化和工程模块化和工程除了基本属性,还需描述模块的内部和外部特性除了基本属性,还需描述模块的内部和外部特性n模块的模块的外部特性外部特性:模块的模块名、参数表:模块的模块名、参数表n模块的模块的内部特性内部特性:完成其功能的程序代码和仅供该模:完成其功能的程序代码和仅供该模块内部使用的数据块内部使
33、用的数据n通常是先确定模块的外部特性通常是先确定模块的外部特性(概要设计概要设计的任务的任务),再,再确定其内部特性确定其内部特性(详细设计详细设计的任务的任务)。怎么样对系统进行模块划分才是好的划分怎么样对系统进行模块划分才是好的划分?41 2.模块化和工程模块化和工程 模模块块划划分分得得越越细细越越好吗?好吗?模块大小、模块数目与费用的关系模块大小、模块数目与费用的关系 2.模块化和工程模块化和工程信息隐藏信息隐藏如何分解一个软件才能得到最佳的模块组合如何分解一个软件才能得到最佳的模块组合?需要了解什么是?需要了解什么是“信息隐藏信息隐藏”。Parnas:每个模块的实现细节对于其它模块:
34、每个模块的实现细节对于其它模块来说是隐蔽的。就是说,模块中所包含的信来说是隐蔽的。就是说,模块中所包含的信息(包括数据和过程)不允许其它不需要这息(包括数据和过程)不允许其它不需要这些信息的模块使用。些信息的模块使用。信息隐藏使得在将来修改软件时偶然引入错信息隐藏使得在将来修改软件时偶然引入错误所造成的影响可以局限在一个或几个模块误所造成的影响可以局限在一个或几个模块内部,不致波及到软件的其它部分。内部,不致波及到软件的其它部分。6.4 软件设计原则4.4.模块的独立性模块的独立性模块独立性是指软件系统中每个模块只涉及软模块独立性是指软件系统中每个模块只涉及软件要求的具体的子功能,而和软件系统
35、中其它件要求的具体的子功能,而和软件系统中其它的模块的接口是简单的。的模块的接口是简单的。一般采用两个准则度量模块独立性,即模块间一般采用两个准则度量模块独立性,即模块间耦合耦合和模块和模块内聚内聚。(。(1978年年Meyer)n耦合是模块之间的互相连接的紧密程度的度耦合是模块之间的互相连接的紧密程度的度量。量。n内聚是模块功能强度内聚是模块功能强度(一个模块内部各个元素一个模块内部各个元素彼此结合的紧密程度彼此结合的紧密程度)的度量。的度量。n模块独立性比较强的模块应是模块独立性比较强的模块应是高内聚低耦合高内聚低耦合的模块。的模块。2.模块化和工程模块化和工程(1)内聚性(内聚性(Coh
36、esion)内聚是模块功能强度(一个模块内部各个元素彼此内聚是模块功能强度(一个模块内部各个元素彼此结合的紧密程度)的度量。一个内聚程度高的模块结合的紧密程度)的度量。一个内聚程度高的模块(在理想情况下)应当只做一件事。一般模块的内(在理想情况下)应当只做一件事。一般模块的内聚性分为七种类型。聚性分为七种类型。模块的内聚度量模块的内聚度量 2.模块化和工程模块化和工程(2)耦合性(耦合性(Coupling)耦合是模块之间的相对独立性(互相连接的紧密程耦合是模块之间的相对独立性(互相连接的紧密程度)的度量。它取决于各个模块之间接口的复杂程度)的度量。它取决于各个模块之间接口的复杂程度、调用模块的
37、方式以及哪些信息通过接口。度、调用模块的方式以及哪些信息通过接口。一般模块之间可能的连接方式有七种,构成耦合性一般模块之间可能的连接方式有七种,构成耦合性的七种类型。的七种类型。模块的耦合性度量模块的耦合性度量 2.模块化和工程模块化和工程C语言中的分块开发语言中的分块开发C语言允许一个程序由多个源文件组成。当语言允许一个程序由多个源文件组成。当程序规模比较大时,可以根据模块化原则将程序规模比较大时,可以根据模块化原则将程序分成多个程序分成多个.c源文件,每个源文件看作是源文件,每个源文件看作是一个模块,每个源文件中可包含一个或多个一个模块,每个源文件中可包含一个或多个功能连接紧密的函数。功能
38、连接紧密的函数。在编译该程序时,可以以源文件为单位分别在编译该程序时,可以以源文件为单位分别进行编译,产生对应的目标文件,然后再用进行编译,产生对应的目标文件,然后再用链接程序将多个目标文件链接成一个可执行链接程序将多个目标文件链接成一个可执行文件。文件。C语言的这种编译过程称为分块编译,语言的这种编译过程称为分块编译,这种开发方法称为分块开发这种开发方法称为分块开发47 C程序由后缀为程序由后缀为.c的源文件和后缀为的源文件和后缀为.h的头文的头文件组成。前者包含实际的程序代码,后者为件组成。前者包含实际的程序代码,后者为.c源文件提供辅助性信息。源文件提供辅助性信息。在确定一个程序要划分成
39、几个源文件,每一在确定一个程序要划分成几个源文件,每一个源文件要包含哪些函数时,需要以提高个源文件要包含哪些函数时,需要以提高模模块独立性块独立性为原则,将相关的功能放在一起,为原则,将相关的功能放在一起,形成一个源文件。形成一个源文件。通常,输入和输出有关的函数放在一个文件通常,输入和输出有关的函数放在一个文件中;主函数单独建立一个文件,其中也可以中;主函数单独建立一个文件,其中也可以包含少数与它关系密切的其他函数的定义包含少数与它关系密切的其他函数的定义48 头文件的确定头文件的确定把所有公用的类型定义(结构、联合或枚举声明),把所有公用的类型定义(结构、联合或枚举声明),公用的宏定义放在
40、适当的头文件中,供各个文件参公用的宏定义放在适当的头文件中,供各个文件参考。考。如果在许多地方都使用一个(些)标准头文件,或如果在许多地方都使用一个(些)标准头文件,或者某个头文件本身需要,则可以把标准头文件包含者某个头文件本身需要,则可以把标准头文件包含到一个自己定义的头文件里供使用。到一个自己定义的头文件里供使用。如果只有一个源文件需要某个标准头文件,则不要如果只有一个源文件需要某个标准头文件,则不要将它放在公共的头文件中,而是让这个源文件直接将它放在公共的头文件中,而是让这个源文件直接包含它,以提高编译效率。包含它,以提高编译效率。对于所有在一个源文件里定义、而在其他文件中使对于所有在一
41、个源文件里定义、而在其他文件中使用的东西(函数原型或者变量的外部声明),都需用的东西(函数原型或者变量的外部声明),都需要在某个头文件中声明,以方便使用。要在某个头文件中声明,以方便使用。49 源文件设计时要注意以下问题:源文件设计时要注意以下问题:每个源文件只包含必要的头文件,不用的东西尽量每个源文件只包含必要的头文件,不用的东西尽量不包含。不包含。如果源文件既要包含标准头文件,又要包含自定义如果源文件既要包含标准头文件,又要包含自定义头文件,则应将标准头文件写在前面,以防止本程头文件,则应将标准头文件写在前面,以防止本程序的局部定义影响标准库文件里的定义。序的局部定义影响标准库文件里的定义
42、。在一个源文件中,所有局部的东西都写在各自的函在一个源文件中,所有局部的东西都写在各自的函数中;所有只在本文件范围内使用的外部变量和函数中;所有只在本文件范围内使用的外部变量和函数,都使用数,都使用static关键字定义为外部静态的。关键字定义为外部静态的。对于多个函数都需要访问的变量,应该根据谁使用对于多个函数都需要访问的变量,应该根据谁使用谁管理的归属原则,分别定义为不同源文件里的外谁管理的归属原则,分别定义为不同源文件里的外部变量。在许多文件中都使用的全局变量,一般在部变量。在许多文件中都使用的全局变量,一般在主程序文件里定义。主程序文件里定义。50 2.模块化和工程模块化和工程实例:猴
43、子选大王实例:猴子选大王如何模块化如何模块化linkNode.h:提供链表结点提供链表结点listNode、别名、别名LISTNODE和和LISTNODEPTR的定义的定义link.c:提供链表处理相关函数提供链表处理相关函数link.h:提供提供link.c中函数的函数原型中函数的函数原型main.c:实现选大王算法实现选大王算法 2.模块化和工程模块化和工程VC6.0下工程的创建下工程的创建模块之间的交互:数据共享、函数调用模块之间的交互:数据共享、函数调用如何实现不同模块之间的数据共享和函数调用如何实现不同模块之间的数据共享和函数调用?-再论函数再论函数52 提纲提纲1.使用线程实现任务
44、并发使用线程实现任务并发2.模块化和工程模块化和工程3.概要设计要点概要设计要点4.时间控制函数时间控制函数5.有限状态自动机解题有限状态自动机解题53 3.概要设计要点概要设计的目的:概要设计的目的:全局把握程序结构:进行程序的模块划分,全局把握程序结构:进行程序的模块划分,设计模块之间如何相互调用来完成程序要求设计模块之间如何相互调用来完成程序要求的功能。的功能。定义关键变量,用来存储各模块共享的数据;定义关键变量,用来存储各模块共享的数据;定义常量。定义常量。设计关键的算法,主要是控制策略,提前对设计关键的算法,主要是控制策略,提前对关键的、较难解决的问题进行处理。关键的、较难解决的问题
45、进行处理。54 3.概要设计要点概要设计主要从以下概要设计主要从以下5个方面考虑:个方面考虑:1.用户界面:界面友好,要能从界面提示信息了解电梯用户界面:界面友好,要能从界面提示信息了解电梯/火车火车/银行的状态和请求。银行的状态和请求。2.自动机模型:电梯自动机模型:电梯/火车火车/营业窗口的行为可以用自动营业窗口的行为可以用自动机模型来描述。绘制状态迁移图,图上需要描述引起机模型来描述。绘制状态迁移图,图上需要描述引起状态迁移的条件,并且要在文档中附加说明进入某状状态迁移的条件,并且要在文档中附加说明进入某状态要做的动作。态要做的动作。3.全局变量:较全面地给出了各个函数要共享的数据。全局
46、变量:较全面地给出了各个函数要共享的数据。4.程序模块化:函数接口说明,函数调用关系说明。程序模块化:函数接口说明,函数调用关系说明。5.调度算法:给出电梯、小火车调度或者银行调度的算调度算法:给出电梯、小火车调度或者银行调度的算法。法。55 3-1用户界面(用户界面(1)点评:界面上增加各层向上向下请求对应的字符,以便于点评:界面上增加各层向上向下请求对应的字符,以便于操作;电梯请求展示区需要再细化,分别显示:向上请求、操作;电梯请求展示区需要再细化,分别显示:向上请求、向下请求,电梯内请求。向下请求,电梯内请求。很漂亮,不过展示的信息太少很漂亮,不过展示的信息太少56 3-1用户界面(用户
47、界面(2)电梯内部描述电梯内部描述57 3-2自动机模型自动机模型思考:该图存在的问题?思考:该图存在的问题?58 3-3全局变量全局变量函数之间如何通信?全局变量或者参数函数之间如何通信?全局变量或者参数线程之间如何通信?全局变量或者参数线程之间如何通信?全局变量或者参数全局变量设计考虑要全面;全局变量设计考虑要全面;应明确给出定义,如:应明确给出定义,如:nintdestLayer;/记录电梯下一目标服务记录电梯下一目标服务楼层楼层59 3-4程序模块化程序模块化应该说明程序的模块结构,应该说明程序的模块结构,包括:包括:整个程序分成哪几个文件整个程序分成哪几个文件?每一个文件里面包含哪些
48、每一个文件里面包含哪些函数?函数原型说明?函数?函数原型说明?图示说明函数调用关系。图示说明函数调用关系。要考虑策略可切换的问题:要考虑策略可切换的问题:如何设计模块,使得当增加如何设计模块,使得当增加一个新的策略时,对现有代一个新的策略时,对现有代码的修改尽量少?码的修改尽量少?进行清楚的描述。进行清楚的描述。60 3-5关键算法关键算法给出调度算法设计给出调度算法设计算法思路要有助于进一步设计,不能很粗略算法思路要有助于进一步设计,不能很粗略61 提纲提纲1.使用线程实现任务并发使用线程实现任务并发2.模块化和工程模块化和工程3.概要设计要点概要设计要点4.时间控制函数时间控制函数5.有限
49、状态自动机解题有限状态自动机解题62 4.1-计时函数while(1)state_trans();();/根据自动机模型决定此刻电梯的状态根据自动机模型决定此刻电梯的状态print_message();/*输出电梯此刻的状态输出电梯此刻的状态*/control();/*根据控制策略确定下一目标楼层,这在根据控制策略确定下一目标楼层,这在state_trans()中要用到中要用到*/time_count();/*时间片推进一个时间片推进一个*/63 4.1-计时函数clock_tclock(void);ANSI标准库中的标准库中的time.h头文件,其中定义了日期和时头文件,其中定义了日期和时间
50、的处理函数。间的处理函数。这个函数返回从这个函数返回从“启动程序启动程序”到到“程序中调用程序中调用clock()函数函数”之间的之间的CPU时钟计时单元(时钟计时单元(clocktick)数,在)数,在MSDN中称之为挂钟时间(中称之为挂钟时间(wal-clock)。其中)。其中clock_t是用来保存时间的数据类型,长整型。是用来保存时间的数据类型,长整型。clocktick:CPU时钟计时单元,时间长短由时钟计时单元,时间长短由CPU控制。控制。一个一个clocktick不是不是CPU的一个时钟周期,而是的一个时钟周期,而是C/C+的一个基本计时单位。的一个基本计时单位。常常量量CLOC