11程序設計實踐6w-線程、模塊化、時間函數(shù)和設計問題
模塊化設計問題模塊化設計問題1 提綱提綱1.使用線程實現(xiàn)任務并發(fā)使用線程實現(xiàn)任務并發(fā)2.模塊化和工程模塊化和工程3.概要設計要點概要設計要點4.時間控制函數(shù)時間控制函數(shù)5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題2 1.使用線程實現(xiàn)任務并發(fā)問題的引出:以電梯控制系統(tǒng)為例問題的引出:以電梯控制系統(tǒng)為例目前能想到的程序主體結構目前能想到的程序主體結構main()while(1)state_trans();/計算此刻電梯的狀態(tài)計算此刻電梯的狀態(tài)print_message();/輸出電梯此刻的狀態(tài)輸出電梯此刻的狀態(tài),包括動畫包括動畫get_input();/接收當前時刻的新輸入(包括新目接收當前時刻的新輸入(包括新目標和新呼叫)標和新呼叫)control();/*根據(jù)控制策略確定下一目標樓層,根據(jù)控制策略確定下一目標樓層,在在state_trans()中要用到中要用到*/time_count();/時間片推進一個時間片推進一個思考:上述結構不合理之處?思考:上述結構不合理之處?3 1.使用線程實現(xiàn)任務并發(fā)上述結構不合理之處上述結構不合理之處:計算和輸出電梯狀態(tài)與接收服務計算和輸出電梯狀態(tài)與接收服務請求是串行的,與現(xiàn)實中的電梯運行不符!請求是串行的,與現(xiàn)實中的電梯運行不符!程序結構的改進:程序結構的改進:從上述代碼中刪除從上述代碼中刪除get_input(),從而實現(xiàn)每隔一小段時,從而實現(xiàn)每隔一小段時間就刷新電梯當前狀態(tài)間就刷新電梯當前狀態(tài)main()while(1)state_trans();/計算此刻電梯的狀態(tài)計算此刻電梯的狀態(tài)print_message();/輸出電梯此刻的狀態(tài)輸出電梯此刻的狀態(tài)control();/*根據(jù)控制策略確定下一目標樓層,這根據(jù)控制策略確定下一目標樓層,這在在state_trans()中要用到中要用到*/time_count();/時間片推進一個時間片推進一個4 1.使用線程實現(xiàn)任務并發(fā)但是,程序必須要能接收電梯服務請求,如何但是,程序必須要能接收電梯服務請求,如何處理服務請求的輸入?處理服務請求的輸入?理想狀態(tài):理想狀態(tài):電梯服務請求的接收和電梯狀態(tài)的計算輸出電梯服務請求的接收和電梯狀態(tài)的計算輸出能同時進行,互不影響能同時進行,互不影響但是,能否實現(xiàn)?但是,能否實現(xiàn)?答案是:使用線程答案是:使用線程電梯狀電梯狀態(tài)計算態(tài)計算和輸出和輸出共享內存區(qū)共享內存區(qū)接收服接收服務請求務請求5 1.使用線程實現(xiàn)任務并發(fā)進程進程一個正在運行的程序的實例,是一個程序在其自身一個正在運行的程序的實例,是一個程序在其自身的地址空間中的一次執(zhí)行活動,例如的地址空間中的一次執(zhí)行活動,例如n用字處理軟件編輯文稿時,同時打開用字處理軟件編輯文稿時,同時打開mp3播放程序聽播放程序聽音樂,這兩個獨立的程序在同時運行,稱為兩個進程音樂,這兩個獨立的程序在同時運行,稱為兩個進程進程是資源申請、調度和獨立運行的單位進程是資源申請、調度和獨立運行的單位6 1.使用線程實現(xiàn)任務并發(fā)線程線程線程是系統(tǒng)分配處理器時間資源的基本單元。對于操作系統(tǒng)線程是系統(tǒng)分配處理器時間資源的基本單元。對于操作系統(tǒng)而言,其調度單元是線程(而言,其調度單元是線程(為線程提供時間片,線程在自己為線程提供時間片,線程在自己的時間片內運行)的時間片內運行)。一個程序中多段代碼同時并發(fā)執(zhí)行,稱為多線程一個程序中多段代碼同時并發(fā)執(zhí)行,稱為多線程n譬如用譬如用word同時打開多個文檔進行編輯,用同時打開多個文檔進行編輯,用IE瀏覽瀏覽器同時訪問多個網站器同時訪問多個網站通過多線程,一個通過多線程,一個進程進程表面上看同時可以執(zhí)行一個以上表面上看同時可以執(zhí)行一個以上的任務的任務并發(fā)并發(fā)7 線程(續(xù))線程(續(xù))一個進程至少包括一個線程(稱為主線程)。一個進程至少包括一個線程(稱為主線程)。一個進程從主線程的執(zhí)行開始進而創(chuàng)建一個一個進程從主線程的執(zhí)行開始進而創(chuàng)建一個或多個附加線程,就是所謂基于多線程的多或多個附加線程,就是所謂基于多線程的多任務。任務。線程自己不擁有系統(tǒng)資源,但它可與同屬一線程自己不擁有系統(tǒng)資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資個進程的其它線程共享進程所擁有的全部資源源8 1.使用線程實現(xiàn)任務并發(fā)在在C程序中要創(chuàng)建線程,可以調用程序中要創(chuàng)建線程,可以調用Windows操作系統(tǒng)提供的創(chuàng)操作系統(tǒng)提供的創(chuàng)建線程的函數(shù)建線程的函數(shù)CreateThread:HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,LPDWORDlpThreadId);LPVOID是一個是一個Void類型的指針,也就是說你可以將任意類類型的指針,也就是說你可以將任意類型的指針賦值給型的指針賦值給LPVOID類型的變量。類型的變量。DWORD是是32位無符號整數(shù)。位無符號整數(shù)。9 1.使用線程實現(xiàn)任務并發(fā)lpThreadAttributes表示創(chuàng)建線程的安全屬性,表示創(chuàng)建線程的安全屬性,NT下有用??少x值下有用。可賦值為為NULL。dwStackSize指定線程棧的尺寸,如果為指定線程棧的尺寸,如果為0則與進程主線程棧相同。則與進程主線程棧相同。lpStartAddress指定線程開始運行的地址。賦值為指向函數(shù)的指針,指定線程開始運行的地址。賦值為指向函數(shù)的指針,即函數(shù)名。該函數(shù)的名稱任意,但函數(shù)類型必須遵照下述聲明形式即函數(shù)名。該函數(shù)的名稱任意,但函數(shù)類型必須遵照下述聲明形式:DWORDWINAPIThreadProc(LPVOIDlpParameter);否則需要進否則需要進行強制類型轉換行強制類型轉換lpParameter表示傳遞給線程的表示傳遞給線程的32位的參數(shù)(數(shù)值或指針)。位的參數(shù)(數(shù)值或指針)。若無若無參數(shù)則賦值為參數(shù)則賦值為NULL。dwCreationFlags表示是否創(chuàng)建后掛起線程表示是否創(chuàng)建后掛起線程(取值取值CREATE_SUSPENDED表示掛起,取值表示掛起,取值0表示創(chuàng)建后立即運行表示創(chuàng)建后立即運行),掛起后調用掛起后調用ResumeThread繼續(xù)執(zhí)行。若不掛起則賦值為繼續(xù)執(zhí)行。若不掛起則賦值為0。lpThreadId用來存放返回的線程用來存放返回的線程ID。DWORDThreadID1=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;11DWORDWINAPIFun1Proc(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#includeintindex=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);return0;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,NULL,0,NULL);HANDLEhThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);CloseHandle(hThread1);CloseHandle(hThread2);Sleep(4000);return0;16 17 線程的同步線程的同步利用互斥對象利用互斥對象(mutex)實現(xiàn)線程的同步,互斥對象能實現(xiàn)線程的同步,互斥對象能夠確保線程擁有對單個資源的互斥訪問權。夠確保線程擁有對單個資源的互斥訪問權。3個操作個操作n互斥對象的創(chuàng)建互斥對象的創(chuàng)建n互斥對象的釋放互斥對象的釋放n互斥對象的請求互斥對象的請求18 互斥對象的創(chuàng)建HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL binitialOwner,LPCTSTR lpNarne)nlpMutexAttributes:可以給該參數(shù)傳遞 NULL值,讓互斥對象使用默認的安全性nbinitialOwner:BOOL類型,指定互斥對象初始的擁有者。如果該值為真,則創(chuàng)建這個互斥對象的線程獲得該對象的所有權;否則,該線程將不獲得所創(chuàng)建的互斥對象的所有權。nlpName:指定互斥對象的名稱。如果此參數(shù)為 NULL.則創(chuàng)建一個匿名的互斥對象。如果調用成功,該函數(shù)將返回所創(chuàng)建的互斥對象的句柄19 互斥對象的釋放BOOL ReleaseMutex(HANDLE hMutex);ReleaseMutex函數(shù)只有一個HANDLE類型的參數(shù),即需要釋放的互斥對象的句柄。該函數(shù)的返回值是BOOL類型,如果函數(shù)調用成功,返回非0值;否則返回0值。20 互斥對象的請求DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);Handle:所請求的互斥對象的句柄。一旦互斥對象處于有信號狀態(tài),則該函數(shù)就返回。如果該互斥對象始終處于無信號狀態(tài),即未通知的狀態(tài),則該函數(shù)就會一直等待,這樣就會暫停線程的執(zhí)行。dwMilliseconds:指定等待的時間間隔,以毫秒為單位。如果指定的時間間隔己過,即使所請求的對象仍處于無信號狀態(tài),WaitForSingleObject函數(shù)也會返回。如果將此參數(shù)設置為0,那么 WaitForSingleObject函數(shù)將測試該對象的狀態(tài)并立即返回;如果將此參數(shù)設置為INFINITE,則該函數(shù)會永遠等待,直到等待的對象處于有信號狀態(tài)才會返回。調用WaitForSingleObject函數(shù)后,該函數(shù)會一直等待,只有在以下兩種情況下才會返回:1)指定的對象變成有信號狀態(tài)。2)指定的等待時間間隔己過。21 HANDLEhMutex;inttickets=100;DWORDWINAPIFun1Proc(LPVOIDpPararneter)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);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 線程在電梯控制系統(tǒng)中的使用線程在電梯控制系統(tǒng)中的使用考慮現(xiàn)實中安裝在電梯考慮現(xiàn)實中安裝在電梯上的軟件:接收電梯服上的軟件:接收電梯服務請求和計算電梯狀態(tài)、務請求和計算電梯狀態(tài)、從而控制電梯的運行是從而控制電梯的運行是并行的并行的因此我們可以考慮在模因此我們可以考慮在模擬電梯控制系統(tǒng)中設計擬電梯控制系統(tǒng)中設計一個線程專門用于接收一個線程專門用于接收電梯服務請求,另一個電梯服務請求,另一個線程實行電梯的狀態(tài)計線程實行電梯的狀態(tài)計算和狀態(tài)輸出算和狀態(tài)輸出電梯狀電梯狀態(tài)計算態(tài)計算和輸出和輸出線程共享內存區(qū)線程共享內存區(qū)接收服接收服務請求務請求電梯狀態(tài)計算和輸出電梯狀態(tài)計算和輸出:從共享內存:從共享內存區(qū)讀取電梯請求,計算下一目標樓區(qū)讀取電梯請求,計算下一目標樓層,從而確定電梯的下一狀態(tài)。層,從而確定電梯的下一狀態(tài)。接收服務請求接收服務請求:接收電梯請求,將:接收電梯請求,將請求保存到內存。請求保存到內存。25 /主線程主線程main()DWORDThreadID;/創(chuàng)建線程(對應于函數(shù)創(chuàng)建線程(對應于函數(shù)input(),用于接收電梯輸入;,用于接收電梯輸入;HANDLEhRead=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)getInput,NULL,0,&ThreadID);/進行變量初始化工作進行變量初始化工作線程在電梯控制系統(tǒng)中的使用線程在電梯控制系統(tǒng)中的使用26 while(1)state_trans();();/根據(jù)自動機模型決定此刻電梯的狀態(tài)根據(jù)自動機模型決定此刻電梯的狀態(tài)print_message();/*輸出電梯此刻的狀態(tài)輸出電梯此刻的狀態(tài)*/control();/*根據(jù)控制策略確定下一目標樓層,這在根據(jù)控制策略確定下一目標樓層,這在state_trans()中要用到中要用到*/time_count();/*時間片推進一個時間片推進一個*/線程在電梯控制系統(tǒng)中的使用線程在電梯控制系統(tǒng)中的使用27 /接收輸入線程接收輸入線程voidgetInput(void)charch;while(1)ch=getchar();/加入代碼:將加入代碼:將ch翻譯成相應請求并保存;翻譯成相應請求并保存;time_count();/時間片推進一個;時間片推進一個;線程在電梯控制系統(tǒng)中的使用線程在電梯控制系統(tǒng)中的使用28 提綱提綱1.使用線程實現(xiàn)任務并發(fā)使用線程實現(xiàn)任務并發(fā)2.軟件設計和模塊化軟件設計和模塊化3.概要設計要點概要設計要點4.時間控制函數(shù)時間控制函數(shù)5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題29 6.1 軟件設計概述軟件設計在開發(fā)階段中的重要性軟件設計在開發(fā)階段中的重要性需求分析模型中的每一個成份都提供了建立需求分析模型中的每一個成份都提供了建立設計模型所需的信息。設計模型所需的信息。根據(jù)數(shù)據(jù)、功能和行為模型來表示的軟件需根據(jù)數(shù)據(jù)、功能和行為模型來表示的軟件需求,采用某種設計方法進行數(shù)據(jù)設計、系統(tǒng)求,采用某種設計方法進行數(shù)據(jù)設計、系統(tǒng)結構設計和過程設計。結構設計和過程設計。軟件需求分析:軟件需求分析:(1)(1)問題的信息域必須被表示和理解。問題的信息域必須被表示和理解。(數(shù)據(jù)模型數(shù)據(jù)模型)(2)(2)軟件將完成的功能必須被定義。軟件將完成的功能必須被定義。(功能模型功能模型)(3)(3)軟件的行為軟件的行為(作為外部事件的結果作為外部事件的結果)必須被表示。必須被表示。(行為模型行為模型)31 數(shù)據(jù)模型數(shù)據(jù)模型問題的信息域包含三個不同的數(shù)據(jù)和控制視圖:問題的信息域包含三個不同的數(shù)據(jù)和控制視圖:(1 1)信息內容和關系)信息內容和關系信息內容表示了個體數(shù)據(jù)和控制對象,它們信息內容表示了個體數(shù)據(jù)和控制對象,它們可和其他的數(shù)據(jù)和控制對象關聯(lián)??珊推渌臄?shù)據(jù)和控制對象關聯(lián)。(2 2)信息流)信息流信息流表示了數(shù)據(jù)和控制在系統(tǒng)中流動時變信息流表示了數(shù)據(jù)和控制在系統(tǒng)中流動時變化的方式。化的方式。(3 3)信息結構)信息結構信息結構表示了各種數(shù)據(jù)和控制項的內部組信息結構表示了各種數(shù)據(jù)和控制項的內部組織。織。功能模型功能模型對進入軟件的信息和數(shù)據(jù)進行變換和處理的模對進入軟件的信息和數(shù)據(jù)進行變換和處理的模塊,它必須至少完成三個常見功能:輸入、處塊,它必須至少完成三個常見功能:輸入、處理和輸出。功能模型從頂層的語境層模型開始,理和輸出。功能模型從頂層的語境層模型開始,經過一系列的細化迭代,越來越多的功能細節(jié)經過一系列的細化迭代,越來越多的功能細節(jié)被發(fā)現(xiàn),直至得到所有系統(tǒng)功能。被發(fā)現(xiàn),直至得到所有系統(tǒng)功能。4.3 軟件需求分析建模的原則和方法行為模型行為模型大多數(shù)軟件對來自外界的事件做出反應,這種大多數(shù)軟件對來自外界的事件做出反應,這種刺激反應特征形成了行為模型的基礎。一個刺激反應特征形成了行為模型的基礎。一個計算機程序總是處于某個計算機程序總是處于某個狀態(tài)狀態(tài):一種外部可觀:一種外部可觀測的行為模式(如等待、運行),它僅當某事測的行為模式(如等待、運行),它僅當某事件發(fā)生時才被改變。件發(fā)生時才被改變。6.1 軟件設計概述 分析模型轉換為軟件設計的映射關系分析模型轉換為軟件設計的映射關系 6.1 軟件設計概述數(shù)據(jù)設計將實體數(shù)據(jù)設計將實體關系圖中描述的對象和關關系圖中描述的對象和關系,以及數(shù)據(jù)詞典中描述的詳細數(shù)據(jù)內容轉系,以及數(shù)據(jù)詞典中描述的詳細數(shù)據(jù)內容轉化為數(shù)據(jù)結構的定義?;癁閿?shù)據(jù)結構的定義。系統(tǒng)結構設計定義軟件系統(tǒng)各主要成份之間系統(tǒng)結構設計定義軟件系統(tǒng)各主要成份之間的關系。接口設計根據(jù)數(shù)據(jù)流圖定義軟件內的關系。接口設計根據(jù)數(shù)據(jù)流圖定義軟件內部各成份之間、軟件與其它協(xié)同系統(tǒng)之間以部各成份之間、軟件與其它協(xié)同系統(tǒng)之間以及軟件與用戶之間的交互機制。及軟件與用戶之間的交互機制。過程設計則是把結構成份轉換成軟件的過程過程設計則是把結構成份轉換成軟件的過程性描述。在編碼階段,根據(jù)這種過程性描述,性描述。在編碼階段,根據(jù)這種過程性描述,生成源程序代碼,最終通過測試得到完整有生成源程序代碼,最終通過測試得到完整有效的軟件。效的軟件。6.1 軟件設計概述軟件設計是開發(fā)階段中最重要的步驟,它是軟件開發(fā)過軟件設計是開發(fā)階段中最重要的步驟,它是軟件開發(fā)過程中質量得以保證的關鍵步驟。程中質量得以保證的關鍵步驟。軟件設計又是將用戶要求準確地轉化成為最終的軟件產軟件設計又是將用戶要求準確地轉化成為最終的軟件產品的唯一途徑。品的唯一途徑。軟件設計是后續(xù)開發(fā)步驟及軟件維護工作的基礎。軟件設計是后續(xù)開發(fā)步驟及軟件維護工作的基礎。軟件設計對后期開發(fā)的質量影響軟件設計對后期開發(fā)的質量影響 6.1 軟件設計概述軟件設計的過程軟件設計的過程從工程管理角度來看,軟件設計分兩步完成。從工程管理角度來看,軟件設計分兩步完成。n概要設計:將軟件需求轉化為數(shù)據(jù)結構和軟概要設計:將軟件需求轉化為數(shù)據(jù)結構和軟件的系統(tǒng)結構模塊;決定每個模塊的功能;件的系統(tǒng)結構模塊;決定每個模塊的功能;決定模塊之間的調用關系,即模塊間傳遞的決定模塊之間的調用關系,即模塊間傳遞的數(shù)據(jù);決定模塊的接口。數(shù)據(jù);決定模塊的接口。n詳細設計:在概要設計基礎上確定如何實現(xiàn)詳細設計:在概要設計基礎上確定如何實現(xiàn)各模塊的內部細節(jié),即對模塊內部的算法和各模塊的內部細節(jié),即對模塊內部的算法和數(shù)據(jù)結構進行設計,產生詳細設計文檔。在數(shù)據(jù)結構進行設計,產生詳細設計文檔。在后續(xù)的編碼階段就可以完全按照詳細設計的后續(xù)的編碼階段就可以完全按照詳細設計的細節(jié)過程來映射到代碼,最終實現(xiàn)整個系統(tǒng)細節(jié)過程來映射到代碼,最終實現(xiàn)整個系統(tǒng) 2.模塊化和工程模塊化和工程將復雜問題分解為若干較小問題,然后再去求解,有將復雜問題分解為若干較小問題,然后再去求解,有助于控制問題的復雜性,利于問題的解決。助于控制問題的復雜性,利于問題的解決。模塊化:把程序劃分成獨立命名且可獨立訪問的模塊,模塊化:把程序劃分成獨立命名且可獨立訪問的模塊,每個模塊完成一個子功能(解決一個子問題),所有每個模塊完成一個子功能(解決一個子問題),所有模塊集成起來構成的整體可完成用戶的所有需求。高模塊集成起來構成的整體可完成用戶的所有需求。高層模塊從整體上把握問題層模塊從整體上把握問題,隱蔽細節(jié)。低層模塊解決細隱蔽細節(jié)。低層模塊解決細節(jié)問題。節(jié)問題。模塊化意義模塊化意義n降低了系統(tǒng)的復雜性,使系統(tǒng)容易修改和重用;降低了系統(tǒng)的復雜性,使系統(tǒng)容易修改和重用;n推動系統(tǒng)各部分的并行開發(fā),提高開發(fā)效率。推動系統(tǒng)各部分的并行開發(fā),提高開發(fā)效率。復雜問題復雜問題較小問題較小問題分解分解39 2.模塊化和工程模塊化和工程模塊的定義模塊的定義一般把用一個名字就可調用的一段程序稱為一般把用一個名字就可調用的一段程序稱為“模塊模塊”,如子程序、函數(shù)等。函數(shù)是最小的模塊,若干,如子程序、函數(shù)等。函數(shù)是最小的模塊,若干個緊密相關的函數(shù)可以組成更大的模塊源文件。個緊密相關的函數(shù)可以組成更大的模塊源文件。模塊的基本屬性:模塊的基本屬性:n功能功能:描述該模塊實現(xiàn)什么功能:描述該模塊實現(xiàn)什么功能n邏輯邏輯:描述模塊內部怎么做:描述模塊內部怎么做n狀態(tài)狀態(tài):該模塊使用時的環(huán)境和條件:該模塊使用時的環(huán)境和條件40 2.模塊化和工程模塊化和工程除了基本屬性,還需描述模塊的內部和外部特性除了基本屬性,還需描述模塊的內部和外部特性n模塊的模塊的外部特性外部特性:模塊的模塊名、參數(shù)表:模塊的模塊名、參數(shù)表n模塊的模塊的內部特性內部特性:完成其功能的程序代碼和僅供該模:完成其功能的程序代碼和僅供該模塊內部使用的數(shù)據(jù)塊內部使用的數(shù)據(jù)n通常是先確定模塊的外部特性通常是先確定模塊的外部特性(概要設計概要設計的任務的任務),再,再確定其內部特性確定其內部特性(詳細設計詳細設計的任務的任務)。怎么樣對系統(tǒng)進行模塊劃分才是好的劃分怎么樣對系統(tǒng)進行模塊劃分才是好的劃分?41 2.模塊化和工程模塊化和工程 模模塊塊劃劃分分得得越越細細越越好嗎?好嗎?模塊大小、模塊數(shù)目與費用的關系模塊大小、模塊數(shù)目與費用的關系 2.模塊化和工程模塊化和工程信息隱藏信息隱藏如何分解一個軟件才能得到最佳的模塊組合如何分解一個軟件才能得到最佳的模塊組合?需要了解什么是?需要了解什么是“信息隱藏信息隱藏”。Parnas:每個模塊的實現(xiàn)細節(jié)對于其它模塊:每個模塊的實現(xiàn)細節(jié)對于其它模塊來說是隱蔽的。就是說,模塊中所包含的信來說是隱蔽的。就是說,模塊中所包含的信息(包括數(shù)據(jù)和過程)不允許其它不需要這息(包括數(shù)據(jù)和過程)不允許其它不需要這些信息的模塊使用。些信息的模塊使用。信息隱藏使得在將來修改軟件時偶然引入錯信息隱藏使得在將來修改軟件時偶然引入錯誤所造成的影響可以局限在一個或幾個模塊誤所造成的影響可以局限在一個或幾個模塊內部,不致波及到軟件的其它部分。內部,不致波及到軟件的其它部分。6.4 軟件設計原則4.4.模塊的獨立性模塊的獨立性模塊獨立性是指軟件系統(tǒng)中每個模塊只涉及軟模塊獨立性是指軟件系統(tǒng)中每個模塊只涉及軟件要求的具體的子功能,而和軟件系統(tǒng)中其它件要求的具體的子功能,而和軟件系統(tǒng)中其它的模塊的接口是簡單的。的模塊的接口是簡單的。一般采用兩個準則度量模塊獨立性,即模塊間一般采用兩個準則度量模塊獨立性,即模塊間耦合耦合和模塊和模塊內聚內聚。(。(1978年年Meyer)n耦合是模塊之間的互相連接的緊密程度的度耦合是模塊之間的互相連接的緊密程度的度量。量。n內聚是模塊功能強度內聚是模塊功能強度(一個模塊內部各個元素一個模塊內部各個元素彼此結合的緊密程度彼此結合的緊密程度)的度量。的度量。n模塊獨立性比較強的模塊應是模塊獨立性比較強的模塊應是高內聚低耦合高內聚低耦合的模塊。的模塊。2.模塊化和工程模塊化和工程(1)內聚性(內聚性(Cohesion)內聚是模塊功能強度(一個模塊內部各個元素彼此內聚是模塊功能強度(一個模塊內部各個元素彼此結合的緊密程度)的度量。一個內聚程度高的模塊結合的緊密程度)的度量。一個內聚程度高的模塊(在理想情況下)應當只做一件事。一般模塊的內(在理想情況下)應當只做一件事。一般模塊的內聚性分為七種類型。聚性分為七種類型。模塊的內聚度量模塊的內聚度量 2.模塊化和工程模塊化和工程(2)耦合性(耦合性(Coupling)耦合是模塊之間的相對獨立性(互相連接的緊密程耦合是模塊之間的相對獨立性(互相連接的緊密程度)的度量。它取決于各個模塊之間接口的復雜程度)的度量。它取決于各個模塊之間接口的復雜程度、調用模塊的方式以及哪些信息通過接口。度、調用模塊的方式以及哪些信息通過接口。一般模塊之間可能的連接方式有七種,構成耦合性一般模塊之間可能的連接方式有七種,構成耦合性的七種類型。的七種類型。模塊的耦合性度量模塊的耦合性度量 2.模塊化和工程模塊化和工程C語言中的分塊開發(fā)語言中的分塊開發(fā)C語言允許一個程序由多個源文件組成。當語言允許一個程序由多個源文件組成。當程序規(guī)模比較大時,可以根據(jù)模塊化原則將程序規(guī)模比較大時,可以根據(jù)模塊化原則將程序分成多個程序分成多個.c源文件,每個源文件看作是源文件,每個源文件看作是一個模塊,每個源文件中可包含一個或多個一個模塊,每個源文件中可包含一個或多個功能連接緊密的函數(shù)。功能連接緊密的函數(shù)。在編譯該程序時,可以以源文件為單位分別在編譯該程序時,可以以源文件為單位分別進行編譯,產生對應的目標文件,然后再用進行編譯,產生對應的目標文件,然后再用鏈接程序將多個目標文件鏈接成一個可執(zhí)行鏈接程序將多個目標文件鏈接成一個可執(zhí)行文件。文件。C語言的這種編譯過程稱為分塊編譯,語言的這種編譯過程稱為分塊編譯,這種開發(fā)方法稱為分塊開發(fā)這種開發(fā)方法稱為分塊開發(fā)47 C程序由后綴為程序由后綴為.c的源文件和后綴為的源文件和后綴為.h的頭文的頭文件組成。前者包含實際的程序代碼,后者為件組成。前者包含實際的程序代碼,后者為.c源文件提供輔助性信息。源文件提供輔助性信息。在確定一個程序要劃分成幾個源文件,每一在確定一個程序要劃分成幾個源文件,每一個源文件要包含哪些函數(shù)時,需要以提高個源文件要包含哪些函數(shù)時,需要以提高模模塊獨立性塊獨立性為原則,將相關的功能放在一起,為原則,將相關的功能放在一起,形成一個源文件。形成一個源文件。通常,輸入和輸出有關的函數(shù)放在一個文件通常,輸入和輸出有關的函數(shù)放在一個文件中;主函數(shù)單獨建立一個文件,其中也可以中;主函數(shù)單獨建立一個文件,其中也可以包含少數(shù)與它關系密切的其他函數(shù)的定義包含少數(shù)與它關系密切的其他函數(shù)的定義48 頭文件的確定頭文件的確定把所有公用的類型定義(結構、聯(lián)合或枚舉聲明),把所有公用的類型定義(結構、聯(lián)合或枚舉聲明),公用的宏定義放在適當?shù)念^文件中,供各個文件參公用的宏定義放在適當?shù)念^文件中,供各個文件參考???。如果在許多地方都使用一個(些)標準頭文件,或如果在許多地方都使用一個(些)標準頭文件,或者某個頭文件本身需要,則可以把標準頭文件包含者某個頭文件本身需要,則可以把標準頭文件包含到一個自己定義的頭文件里供使用。到一個自己定義的頭文件里供使用。如果只有一個源文件需要某個標準頭文件,則不要如果只有一個源文件需要某個標準頭文件,則不要將它放在公共的頭文件中,而是讓這個源文件直接將它放在公共的頭文件中,而是讓這個源文件直接包含它,以提高編譯效率。包含它,以提高編譯效率。對于所有在一個源文件里定義、而在其他文件中使對于所有在一個源文件里定義、而在其他文件中使用的東西(函數(shù)原型或者變量的外部聲明),都需用的東西(函數(shù)原型或者變量的外部聲明),都需要在某個頭文件中聲明,以方便使用。要在某個頭文件中聲明,以方便使用。49 源文件設計時要注意以下問題:源文件設計時要注意以下問題:每個源文件只包含必要的頭文件,不用的東西盡量每個源文件只包含必要的頭文件,不用的東西盡量不包含。不包含。如果源文件既要包含標準頭文件,又要包含自定義如果源文件既要包含標準頭文件,又要包含自定義頭文件,則應將標準頭文件寫在前面,以防止本程頭文件,則應將標準頭文件寫在前面,以防止本程序的局部定義影響標準庫文件里的定義。序的局部定義影響標準庫文件里的定義。在一個源文件中,所有局部的東西都寫在各自的函在一個源文件中,所有局部的東西都寫在各自的函數(shù)中;所有只在本文件范圍內使用的外部變量和函數(shù)中;所有只在本文件范圍內使用的外部變量和函數(shù),都使用數(shù),都使用static關鍵字定義為外部靜態(tài)的。關鍵字定義為外部靜態(tài)的。對于多個函數(shù)都需要訪問的變量,應該根據(jù)誰使用對于多個函數(shù)都需要訪問的變量,應該根據(jù)誰使用誰管理的歸屬原則,分別定義為不同源文件里的外誰管理的歸屬原則,分別定義為不同源文件里的外部變量。在許多文件中都使用的全局變量,一般在部變量。在許多文件中都使用的全局變量,一般在主程序文件里定義。主程序文件里定義。50 2.模塊化和工程模塊化和工程實例:猴子選大王實例:猴子選大王如何模塊化如何模塊化linkNode.h:提供鏈表結點提供鏈表結點listNode、別名、別名LISTNODE和和LISTNODEPTR的定義的定義link.c:提供鏈表處理相關函數(shù)提供鏈表處理相關函數(shù)link.h:提供提供link.c中函數(shù)的函數(shù)原型中函數(shù)的函數(shù)原型main.c:實現(xiàn)選大王算法實現(xiàn)選大王算法 2.模塊化和工程模塊化和工程VC6.0下工程的創(chuàng)建下工程的創(chuàng)建模塊之間的交互:數(shù)據(jù)共享、函數(shù)調用模塊之間的交互:數(shù)據(jù)共享、函數(shù)調用如何實現(xiàn)不同模塊之間的數(shù)據(jù)共享和函數(shù)調用如何實現(xiàn)不同模塊之間的數(shù)據(jù)共享和函數(shù)調用?-再論函數(shù)再論函數(shù)52 提綱提綱1.使用線程實現(xiàn)任務并發(fā)使用線程實現(xiàn)任務并發(fā)2.模塊化和工程模塊化和工程3.概要設計要點概要設計要點4.時間控制函數(shù)時間控制函數(shù)5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題53 3.概要設計要點概要設計的目的:概要設計的目的:全局把握程序結構:進行程序的模塊劃分,全局把握程序結構:進行程序的模塊劃分,設計模塊之間如何相互調用來完成程序要求設計模塊之間如何相互調用來完成程序要求的功能。的功能。定義關鍵變量,用來存儲各模塊共享的數(shù)據(jù);定義關鍵變量,用來存儲各模塊共享的數(shù)據(jù);定義常量。定義常量。設計關鍵的算法,主要是控制策略,提前對設計關鍵的算法,主要是控制策略,提前對關鍵的、較難解決的問題進行處理。關鍵的、較難解決的問題進行處理。54 3.概要設計要點概要設計主要從以下概要設計主要從以下5個方面考慮:個方面考慮:1.用戶界面:界面友好,要能從界面提示信息了解電梯用戶界面:界面友好,要能從界面提示信息了解電梯/火車火車/銀行的狀態(tài)和請求。銀行的狀態(tài)和請求。2.自動機模型:電梯自動機模型:電梯/火車火車/營業(yè)窗口的行為可以用自動營業(yè)窗口的行為可以用自動機模型來描述。繪制狀態(tài)遷移圖,圖上需要描述引起機模型來描述。繪制狀態(tài)遷移圖,圖上需要描述引起狀態(tài)遷移的條件,并且要在文檔中附加說明進入某狀狀態(tài)遷移的條件,并且要在文檔中附加說明進入某狀態(tài)要做的動作。態(tài)要做的動作。3.全局變量:較全面地給出了各個函數(shù)要共享的數(shù)據(jù)。全局變量:較全面地給出了各個函數(shù)要共享的數(shù)據(jù)。4.程序模塊化:函數(shù)接口說明,函數(shù)調用關系說明。程序模塊化:函數(shù)接口說明,函數(shù)調用關系說明。5.調度算法:給出電梯、小火車調度或者銀行調度的算調度算法:給出電梯、小火車調度或者銀行調度的算法。法。55 3-1用戶界面(用戶界面(1)點評:界面上增加各層向上向下請求對應的字符,以便于點評:界面上增加各層向上向下請求對應的字符,以便于操作;電梯請求展示區(qū)需要再細化,分別顯示:向上請求、操作;電梯請求展示區(qū)需要再細化,分別顯示:向上請求、向下請求,電梯內請求。向下請求,電梯內請求。很漂亮,不過展示的信息太少很漂亮,不過展示的信息太少56 3-1用戶界面(用戶界面(2)電梯內部描述電梯內部描述57 3-2自動機模型自動機模型思考:該圖存在的問題?思考:該圖存在的問題?58 3-3全局變量全局變量函數(shù)之間如何通信?全局變量或者參數(shù)函數(shù)之間如何通信?全局變量或者參數(shù)線程之間如何通信?全局變量或者參數(shù)線程之間如何通信?全局變量或者參數(shù)全局變量設計考慮要全面;全局變量設計考慮要全面;應明確給出定義,如:應明確給出定義,如:nintdestLayer;/記錄電梯下一目標服務記錄電梯下一目標服務樓層樓層59 3-4程序模塊化程序模塊化應該說明程序的模塊結構,應該說明程序的模塊結構,包括:包括:整個程序分成哪幾個文件整個程序分成哪幾個文件?每一個文件里面包含哪些每一個文件里面包含哪些函數(shù)?函數(shù)原型說明?函數(shù)?函數(shù)原型說明?圖示說明函數(shù)調用關系。圖示說明函數(shù)調用關系。要考慮策略可切換的問題:要考慮策略可切換的問題:如何設計模塊,使得當增加如何設計模塊,使得當增加一個新的策略時,對現(xiàn)有代一個新的策略時,對現(xiàn)有代碼的修改盡量少?碼的修改盡量少?進行清楚的描述。進行清楚的描述。60 3-5關鍵算法關鍵算法給出調度算法設計給出調度算法設計算法思路要有助于進一步設計,不能很粗略算法思路要有助于進一步設計,不能很粗略61 提綱提綱1.使用線程實現(xiàn)任務并發(fā)使用線程實現(xiàn)任務并發(fā)2.模塊化和工程模塊化和工程3.概要設計要點概要設計要點4.時間控制函數(shù)時間控制函數(shù)5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題62 4.1-計時函數(shù)while(1)state_trans();();/根據(jù)自動機模型決定此刻電梯的狀態(tài)根據(jù)自動機模型決定此刻電梯的狀態(tài)print_message();/*輸出電梯此刻的狀態(tài)輸出電梯此刻的狀態(tài)*/control();/*根據(jù)控制策略確定下一目標樓層,這在根據(jù)控制策略確定下一目標樓層,這在state_trans()中要用到中要用到*/time_count();/*時間片推進一個時間片推進一個*/63 4.1-計時函數(shù)clock_tclock(void);ANSI標準庫中的標準庫中的time.h頭文件,其中定義了日期和時頭文件,其中定義了日期和時間的處理函數(shù)。間的處理函數(shù)。這個函數(shù)返回從這個函數(shù)返回從“啟動程序啟動程序”到到“程序中調用程序中調用clock()函數(shù)函數(shù)”之間的之間的CPU時鐘計時單元(時鐘計時單元(clocktick)數(shù),在)數(shù),在MSDN中稱之為掛鐘時間(中稱之為掛鐘時間(wal-clock)。其中)。其中clock_t是用來保存時間的數(shù)據(jù)類型,長整型。是用來保存時間的數(shù)據(jù)類型,長整型。clocktick:CPU時鐘計時單元,時間長短由時鐘計時單元,時間長短由CPU控制??刂?。一個一個clocktick不是不是CPU的一個時鐘周期,而是的一個時鐘周期,而是C/C+的一個基本計時單位。的一個基本計時單位。常常量量CLOCKS_PER_SEC,它它表表示示一一秒秒鐘鐘會會有有多多少少個個時鐘計時單元時鐘計時單元??煽梢砸允故褂糜霉绞絚lock()/CLOCKS_PER_SEC來來計計算算一一個個進程自身的運行時間進程自身的運行時間。64 4.1-計時函數(shù)#include“stdio.h”#include“stdlib.h”#include“time.h”intmain(void)longi=10000000L;clock_tstart,finish;doubleduration;/*測量一個事件持續(xù)的時間測量一個事件持續(xù)的時間*/printf(“Timetodo%ldemptyloopsis”,i);start=clock();while(i-);/什么都不做什么都不做finish=clock();duration=(double)(finish-start)/CLOCKS_PER_SEC;printf(%fsecondsn,duration);system(pause);65 4.1-計時函數(shù)下述循環(huán)將持續(xù)運行下述循環(huán)將持續(xù)運行1秒鐘秒鐘clock_tstart,finish;doubleduration;start=clock();duration=0.0;while(duration1.0)finish=clock();duration=(double)(finish-start)/CLOCKS_PER_SEC;66 4.2-線程休眠函數(shù)線程休眠函數(shù):線程休眠函數(shù):Sleep(int);Sleep(1000)Windows下表示下表示1000毫秒,也就是毫秒,也就是1秒鐘;秒鐘;Linux下表示下表示1000秒,秒,Linux下使用毫秒級別的函數(shù)可以使用下使用毫秒級別的函數(shù)可以使用usleep。Sleep函數(shù)是使調用函數(shù)是使調用Sleep函數(shù)的線程休眠,線程主動放棄時間片。當經過函數(shù)的線程休眠,線程主動放棄時間片。當經過指定的時間間隔后,再啟動線程,繼續(xù)執(zhí)行代碼。指定的時間間隔后,再啟動線程,繼續(xù)執(zhí)行代碼。Sleep函數(shù)并不能起到函數(shù)并不能起到定時的作用,主要作用是延時。定時的作用,主要作用是延時。#include#include /注意頭文件一定要包含int main()int i=9;while(i1)Sleep(1000);/線程被掛起1秒,相當于機器運行到這里停頓1秒,再繼續(xù)向下運行;printf(A);i-;system(pause);return 0;67 提綱提綱1.使用線程實現(xiàn)任務并發(fā)使用線程實現(xiàn)任務并發(fā)2.模塊化和工程模塊化和工程3.概要設計要點概要設計要點4.時間控制函數(shù)時間控制函數(shù)5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題68 5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題第一步:畫出狀態(tài)轉換圖第一步:畫出狀態(tài)轉換圖第二步:針對每一種狀態(tài),進一步分別分析:第二步:針對每一種狀態(tài),進一步分別分析:當輸入為字符當輸入為字符1時,需要做什么工作,下一個狀態(tài)時,需要做什么工作,下一個狀態(tài)是什么是什么n如刪除注釋一題中要做的工作就是往目標文件中如刪除注釋一題中要做的工作就是往目標文件中寫入非注釋部分的字符;寫入非注釋部分的字符;當輸入為字符當輸入為字符2時,需要做什么工作,下一個狀態(tài)時,需要做什么工作,下一個狀態(tài)是什么;是什么;第三步:編碼。將要做的工作和狀態(tài)的遷移用代碼表第三步:編碼。將要做的工作和狀態(tài)的遷移用代碼表示出來示出來69 練習:練習:輸入一個字符串,以回車結束;判斷該字符串是否是合輸入一個字符串,以回車結束;判斷該字符串是否是合法的法的C語言整型變量定義。語言整型變量定義。C語言變量名必須由下劃線、字母或者數(shù)字組成,且第一語言變量名必須由下劃線、字母或者數(shù)字組成,且第一個字符是下劃線或者字母。個字符是下劃線或者字母。int_myVariable,count;/合法合法int8Count;/非法非法要求:先畫出本題的有限狀態(tài)自動機轉換圖,然后書寫要求:先畫出本題的有限狀態(tài)自動機轉換圖,然后書寫程序。程序。5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題70 5.有限狀態(tài)自動機解題有限狀態(tài)自動機解題程序程序71 q1:最近接收到的是:最近接收到的是/q5:已收到:已收到/,注釋已經開注釋已經開始始q4:非注釋狀態(tài)非注釋狀態(tài)q3:等待等待/以結束注釋以結束注釋q2:已收到:已收到/*,注釋已經開始注釋已經開始上機題講解上機題講解【源程序源程序】72 電梯系統(tǒng)的電梯系統(tǒng)的state_trans函數(shù)設計函數(shù)設計73 電梯系統(tǒng)的電梯系統(tǒng)的state_trans函數(shù)設計函數(shù)設計voidstate_trans()switch(state)/*根據(jù)狀態(tài)和事件根據(jù)狀態(tài)和事件,進行相應的處理,進行相應的處理*/caseIDLE:if(存在其他樓層的呼叫事件存在其他樓層的呼叫事件)/執(zhí)行相關工作;執(zhí)行相關工作;state=RUN;if(存在本樓層的呼叫事件存在本樓層的呼叫事件)/執(zhí)行相關工作;執(zhí)行相關工作;state=STOP;/*電梯狀態(tài)設置為??侩娞轄顟B(tài)設置為???/break;74 電梯系統(tǒng)的電梯系統(tǒng)的state_trans函數(shù)設計函數(shù)設計caseRUN:.break;caseSTOP:.break;default:break;75 76