Download presentation
Presentation is loading. Please wait.
1
Visual C++ Windows Programming
第十一章 訊息處理與多執行緒 台大電機系 李奇錚
2
大綱 Windows 訊息處理 多執行緒程式設計
3
記憶體 可定址的記憶體 虛擬記憶體 利用虛擬記憶體的概念來解決記憶體不足的問題 每一個程式會透過OS來對記憶體進行存取
兩個不同程式會使用單獨的記憶體空間,並且雙方不能互相存取對方的記憶體空間 程式在使用記憶體前,要先向OS做申請
4
記憶體 每個process可以看成是有一個專屬的OS在為其服務,但事實上,這些OS是OS的本身間分身 Process可以自由使用的記憶體空間
Process可以自由使用的記憶體空間 系統DLL記憶體映射檔 OS kernel 2GB 3GB 4GB WinNT的process使用記憶體情況
5
虛擬MS-DOS Win16 Global Heap
記憶體 虛擬MS-DOS Win16 Global Heap Process可以自由使用的記憶體空間 系統DLL記憶體映射檔 Win 16 Global Heap OS kernel 4MB 2GB 3GB 4GB
6
記憶體 C, 利用malloc() , free()的方式來向OS要記憶體 C++ 利用new , delete的方式來要記憶體
系統扣除掉所需要的空間後,剩下的空間稱”free memory” 或heap , 而malloc就是要求OS自heap中割出一塊來給使用者使用 OS收到malloc後,就由heap中割出一塊空間給使用者,然後將這塊記憶體的指標(或是說位址)回傳
7
記憶體 Win32的記憶體配置 HGLOBAL GlobalAlloc(UNIT fuAlloc , DWORD cbAlloc)
要求OS配置一塊記憶體 fuAlloc : 記憶體的屬性 GHND , GMEM_DISCARDABLE , GEME_FIXED , GMED_MOVEABLE , GMEM_NOCOMPACT, GMEM_DDESHARE, GMEM_SHARE… cbAlloc : 配置記憶體的大小,單位是byte 記憶體的屬性 : Discardable memory : 可丟棄記憶體 , 即當主記憶體用盡時,會把那些很久沒有動過的記憶區塊丟到磁碟上,等到要的時候在把他們讀進主記憶體中,而設定成Discardable 就表示這種記憶體可以丟到磁碟上去(也就是較不重要的) Fixed memory : 固定記憶體 , 當記憶體沒有被鎖定時,就可以被OS自由的搬動,以減低碎裂的情況發生, 但fixed這種記憶體是一種例外,也就是這種記憶體,即使記憶體沒有被鎖定,也不能任意搬動 Moveable memory : 可移動記憶體 , 和固定式的相反 , 即只要記憶體沒被鎖定,就可以自由的搬動
8
記憶體 Win32的記憶體配置 GlobalLock( HGLOBAL hglb) GlobalUnlock( HGLOBAL hglb)
鎖定記憶體區塊 GlobalUnlock( HGLOBAL hglb) 解除鎖定記憶體區塊 GlobalFree(HGLOBAL hglb) 釋放記憶體區塊 GlobalFlags(HGLOBAL hglb) 傳回與指定記憶區塊有關的資訊,以及被鎖定的次數 回傳值的低位元組是被鎖定的次數,也可以利用AND的方式來檢查是否包含下面的旗號 GMEM_DISCARDABLE :可以被丟棄 , GMEM_DISCARED : 已經被丟棄 GlobalSize(HGLOBAL hglb) 指定記憶體區塊的大小 回傳值是0 : handle給的不對,或是已經被丟到磁碟上了 GlobalReAlloc(HGLOBAL hglb , DWORD cbNewSize , UNIT fuAlloc) 更改記憶體區塊的大小
9
記憶體 Win32的記憶體配置 void FAR* lpBuffer hglb = GlobalAlloc( GPTR, 1024);
lpBuffer = Globallock(hglb) …. GlobalUnlock(hglb); GlobalFree(hglb);
10
Windows 訊息處理 Processes vs. Threads
當我們將一個應用程式載入到記憶體中執行時,該應用程式在記憶體中執行的狀態,一般而言我們統稱之為 process 。更精確的說法是,當我們將應用由硬碟載入記憶體時,系統分配給該應用程式的記憶體空間,程式碼,以及其他系統資源,這些資源的所有權我們將之視為一個 process 。當系統執行了不只一個 process 時,電腦將以 process 做為電腦分配資源的單位。 Process 是系統分配資源的單位,但是 CPU 時間的分配並非依照 process 來進行。真正分配 CPU 時間的基本單位是 thread 。 在每一個 process 裡,一開始只有一個 thread ,此 thread 稱為 primary thread 。而每一個 thread 還可以產生其他的 thread ,前者為 parent thread ,其他則為 child thread 。
11
Windows 訊息處理 (續) Processes vs. Threads (續)
Windows 所號稱的多工環境,基本上並不是真正的多工,因為系統實際上只有一個或者固定數目的 CPU 。在單 CPU 的系統中,所謂的多工是靠一個 CPU 搭配一個分配 CPU 時間的計時器,將 CPU 的計算時間分配給系統中執行的 thread ,由於在 thread 之間切換執行,分配 CPU 時間的速度很快,所以讓人感覺好像每個 thread 同時被執行,以達到多工的「效果」。 當 process 由一個以上的 threads 所組成時,我們稱這個 process 為多執行緒 (multi-threading) 程式。
12
Processes 產生Child Process
CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) lpApplicationName : 可執行檔的完整路徑名稱,包函結尾的延伸檔名( ex .exe) lpCommandLine : 命令列參數,若將lpApplicationName 設成null的話,就要在此加入執行檔的檔名( 就如在command line打入的指定一樣) lpProcessAttributes : 一些和系統安全相關的資訊 lpThreadAttributes : 和lpProcessAttributes 差不多,不過這個是針對Thread的 bInheritHandles : 決定childe process是否繼承parent process的資源 dwCreationFlags : 設定一些特性的選項,以及child process的優先執行權 lpEnvironment : 環境變數字串 lpCurrentDirectory : 工作目錄 lpStartupInfo : 啟動資訊(存放在STARUPINFO中) lpProcessInformation : CreateProcess會把與child process有關的資訊填在PROCESS_INFOMATION中,並回傳此值
13
dwCreationFlags dwCreationFlags HIGH_PRIORITY_CLASS 最優先執行權
IDLE_PRIORITY_CLASS 系統空閒時再執行 REALTIME_PRIORITY_CLASS 即時執行 CREATE_NEW_CONSOLE 另外開啟一個console來執行child process CREATE_SUSPENDED 產生child process後,不立刻執行,等到呼叫ResumeThread()才開始執行 DEBUG_PROCESS 可以對child process執行除錯 …..
14
STARUPINFO struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO;
15
PROCESS_INFORMATION typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
16
Processes PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo;
siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.lpReserved = NULL ; siStartInfo.lpReserved2 = NULL ; siStartInfo.cbReserved2 = 0; siStartInfo.lpDesktop = NULL ; siStartInfo.dwFlags = 0; BOOL ret = CreateProcess(“notepad.exe” , “c:\\config.sys” , NULL , NULL , TRUE , 0 , NULL , NULL , &siStartInfo , &piProcInfo);
17
Processes 和process有關的資訊 終結process GetCurrentProcess(VOID)
回傳process的handle GetCurrentProcessID(VOID) 回傳process的ID ID是一組數字,Win32保證不會有第二個執行的process擁有這個相同的ID GetPriorityClass( HANDLE hProcess) 取得process的priority SetPriorityClass(HANDLE hProcess , DWORD dwPriorityClass) 設定process的priority 終結process ExitProcess() or exit() ExitProcess( UNIT uExitCode) 若process A想要讓process B停止的話,可以在取得process B的handle後呼叫此來做到
18
Thread Thread 一開始用fork() 會建出一個和元parent process一樣大的child process
但很多時候,我們不需要整個process都copy一份來執行,相對的,僅需要拿出一部分來執行就可以了,也就是建一個比較簡化的process 這些process再同一個記憶體空間裡執行,但各自有各自的Stack 像這種process就叫做light process 或稱thread 每個process可以有一個或一個以上的thread,所以process是分配資源的單位,而thread才是CPU執行的最小單位
19
Thread Thread 產生Child Thread CreateThread() or 利用AfxBeginThread()
AfxBeginThread()除了建立一個thread以外,還會建立一些thread相關的資訊 Thread資訊 GetCurrentThread(VOID) GetCurrentThreadID(VOID) GetThreadPriorty( HANDLE hThread) SetThreadPriority (HANDLE hThread , int nPriority) 終止Thread TerminateThread(HANDLE hThread) or _endthread()
20
Thread Thread1 Critical Section Thread2 Thread3 同步化 (Synchronization)
可以想像成是一個房間,同一個process中的每一個thread要存取資源,就必須要進到這個房間才能存取資源 達到同步化的一種方式,對於資源的使用,每一個thread必須先進入一個critical section才能取用該資源 InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection) 初始化一個Critical Section物件, EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 若有多個thread同時呼叫此函式想要進入同一個Critical Section,OS會做選擇並只讓一個thread進入,而其他thread就必須等到該htread結束後,才能進入Critical Section,也就是其他thread會被blocking住 TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 詢問目前Critical Section的狀態 LeaveEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 當一個thread進入Critical Section執行結束後,要呼叫此函式告知OS,否則其他被blocking的thread將無法使用該Critical Section DeleteEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 若要刪除一個Critical Section,可以利用此函式 Thread1 Try Critical Section Thread2 Try Thread3 Try
21
Thread 同步化 (Synchronization) Mutex
不同於利用Critical Section的方式,利用Mutex的話,可以讓不同process的不同thread都參予資源的競爭 就如同Critical Section一樣,要能夠使用資源的thread,就要先取得Mutex才能使用資源 無名Mutex (unnamed Mutex) & 具名Mutex (named Mutex) CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes , BOOL bInitialOwner , LPCTSTR lpName) lpMutexAttributes : 和系統的安全設定有關 bInitialOwner : 若是true,表示建立起mutex的那個thread一開始就擁有這個mutex lpName : 這個mutex的名稱,若是採用此參數,即為一個具名Mutex 對於和建立起這個mutex的thread同process的thread來說,可以直接透過此mutex的handle來存取這個mutex 但對於那些不同process的thread呢?只好呼叫OpenMutex來存取mutex OpenMutex( DWORD dwDesiredAccess , BOOL bInheritHandle , LPCTSTR lpName) dwDesiredAccess : 是存取的方式,通常設為MUTEX_ALL_ACCESS即可 bInheritHandle : 決定child process是否也可以使用這個OpenMutex()所傳回來的handle lpName : 就是該mutex的名稱 ReleaseMutex( HANDLE hMutex) 釋放mutex,如此其他thread才能使用到
22
Thread Thread1 Access Resource Thread2 Getting Mutex Thread3 Thread4
23
Thread 同步化 (Synchronization) Event
Event功能就有點像紅綠燈,如果thread是車子的話,那就要交給Event來控制是否一台車可以通行(存取資源) 自動和手動兩種 自動 : 每次只放一台車通過,隨即轉成紅燈 手動 : 變成綠燈後,可以放所有的車子通過,且燈號不變,直到手動將燈號改變為止 CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes , BOOL bManualReset , BOOL bInitialState , LPCTSTR lpName) lpEventAttributes : 設定安全管制 bManualReset : 表示為自動或手動 bInitialState : 代表一開始的狀態,及是否要亮綠燈 lpName : event的名稱 也分兩種 : 具名和匿名,同樣的,用具名的方式,可以利用OpenEvent讓其他process的thread作存取 OpenEvent( DWORD dwDesiredAccess , BOOL bInheritHandle , LPCTSTR lpName) SetEvent( HANDLE hEvent) 將event設為綠燈 PulseEvent() ResetEvent(HANDLE hEvent) 因為手動的event不會自動做綠轉紅的動作,所以要交給這個函式來李楚
24
Thread Event Ex. CEvent myEvent; void func() { myEvent.Lock(); //……
myEvent.SetEvent(); }
25
Thread 同步化 (Synchronization) CSemaphore
利用CSemaphore可以用來管制某一段程式,讓其在同一時間內.只能被指定數目的thread執行 利用Lock , Unlock的方式來做 CSemaphore DempSemaphore(iCount , lMaxCount , “Demo”) void func() { DempSemaphore.Lock(); //……………. DempSemaphore.Unlock(); }
26
Thread 同步化 (Synchronization) Deadlock & starvation
27
Thread 等待物件狀態改變 DWORD WaitForSingleObject( HANDLE hHANDLE , DWORD dwMilliseconds) 這個函式是用來等待hHandle的狀態發生變化,如果在dwMilliseconds個微秒內,他所等待的事件沒有發生改變,就不等了!! dwMilliseconds可以射程INFINITE,做無限期等待 可以檢查WaitForSingleObject()的回傳值來確認是哪一種條件成立而返回 WAIT_ABANDONED : 若等待的對象是mutex則可能回傳此 WAIT_OBJECT_0 : 等待的條件成立 WAIT_TIMEOUT :愈時 DWORD WaitForMultipleObject( DWORD nCount , CONST HANDLE *lpHANDLE , BOOL bWaitAll , DWORD dwMilliseconds) 可以同時等待數個不同的物件 lpHANDLE : HANDLE的陣列 nCount : 陣列長度 回傳型態 WAIT_OBJECT_0+k : k是一個數字,表示第k個物件狀態改變了!! WAIT_ABANDONED_0+k : k一樣是個數字,表示第k個mutex物件放棄了
28
Thread GUI Thread & 非GUI Thread GUI Thread 凡是在thread中會建立視窗的
會有分的原因在於’訊息佇列’ ’訊息佇列’是相當耗資源的,因此盡量能免則免
29
Thread 非GUI Thread 一般都是利用AfxBeginThread()來建立 可以輕易的轉成GUI Thread
30
Thread 也可以利用CWinThread產生Thread Step1 : 建立一個衍生自CWinThread的新類別
Step2 : 利用CWinThread::CreateThread()來建立新Thread CreateThread(DWORD dwCreateStyle = 0 , UNIT nStackSize = 0 , LPSECURITY_ATRIBUTES lpsecurityAttrs = NULL) dwCreateStyle 設定為CREATE_SUSPENED的話,那這個thread產生之後會先暫停不動,直到程式在呼叫ResumeThread()才會起來 nStackSize : Stack的初始大小,設為0是表示和父thread一樣多 Step3 : 利用CWinThread:: InitInstance()開始執行 必須override這個函式 m_pMainWnd : 建立起來thread主視窗的指標 利用ExitInstance()離開
31
Thread 利用標準函式庫所提供的函式來建立thread _beginthread() _endthread()
32
Windows 訊息處理 (續) 單執行緒 (single-threading) 程式的訊息處理
在典型的 Windows 程式中,主執行緒會具備一個訊息處理迴圈的執行片段 (參考第一章的範例) ,藉由訊息偵測函式 GetMessage() 決定是否有訊息被處理。若沒有任何訊息要處理,則程式會被暫停 (suspend) ,讓其他程式執行。 倘若有訊息要被處理,則會接著執行 TranslateMessage() 函式,將鍵盤訊息轉換為輸入訊息之後,接著執行 DispatchMessage() 函式。這個函式會呼叫程式中的訊息處理函式,而訊息處理函式則會依據訊息的不同加以處理。處理完之後, DispatchMessage() 才會返回。 在 MFC 的架構中, MFC 會幫我們架構訊息處理函式,因此我們只要在訊息映射表中適當地填空,MFC 就會幫我們根據訊息呼叫適當的函式。
33
Windows 訊息處理 (續) 單執行緒程式的控制權釋放
假定我們的程式中有一個會持續耗用相當大的 CPU 資源的計算 (例如求一個 1000x1000 大小矩陣的反矩陣,或者是求 RSA-2048 密碼系統某明文的對應密文) 。在過去的 Windows 3.1 的時代,這段計算可能使得電腦停止回應﹔而即使是今日的 32-bit Windows 系統,這段計算仍然可能使得我們的程式停止回應。 為了避免我們的電腦或程式停止回應,我們可以在這個複雜計算的核心加入一小段與我們的訊息迴圈有類似行為的一段敘述。 PeekMessage() 函式運作方式和 GetMessage() 相當類似,但是它只會檢查該瞬間程式的訊息佇列 (message queue) 中是否有任何訊息,若有訊息則將訊息傳回,而沒有訊息的時候也會立即返回並告知程式無訊息。
34
Windows 訊息處理 (續) 計時器在單執行緒程式中的多工效果
Windows 的計時器 (timer) 功能能夠讓程式每隔固定時間就收到訊息。我們可以藉由處理計時器的函式,使得我們看起來具有多工的效果。 計時器的使用方法相當簡單,不論是直接使用 SDK 或者在 MFC 中。在 MFC 中,我們只要呼叫 CWnd 的成員函式 SetTimer() ,並且利用 ClassWizard 的幫助建立處理WM_TIMER 訊息的函式。一旦你啟動計時器時, WM_TIMER 訊息會不斷地送到你的視窗中,直到你呼叫 CWnd::KillTimer() 函式,或者計時器的視窗被 destroy 為止。 必要時,你可以使用數個計時器,並以整數值識別之。要注意的是,因為 Windows 並非即時作業系統,若你指定的時間間隔小於 100ms ,可能會不夠精確。
50
*Dlg.h ... class CExam11_1Dlg : public CDialog { // Implementation
private: int m_nTimer; int m_nCount; enum { nMaxCount = }; };
51
*Dlg.cpp ... CExam11_1Dlg::CExam11_1Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CExam11_1Dlg::IDD, pParent) { //{{AFX_DATA_INIT(CExam11_1Dlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_nCount = 0; } void CExam11_1Dlg::OnStart() // TODO: Add your control notification handler code here MSG message; m_nTimer = SetTimer(1, 100, NULL); ASSERT(m_nTimer != 0); GetDlgItem(IDC_START)->EnableWindow(FALSE); volatile int nTemp1, nTemp2; for(m_nCount = 0; m_nCount < nMaxCount; m_nCount++) { for(nTemp1 = 0; nTemp1 < 100; nTemp1++) { for(nTemp2 = 0; nTemp2 < 10000; nTemp2++) {
52
*Dlg.cpp if(::PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
::TranslateMessage(&message); ::DispatchMessage(&message); } CDialog::OnOK(); void CExam11_1Dlg::OnCancel() { // TODO: Add extra cleanup here if(m_nCount == 0) { CDialog::OnCancel(); } else { m_nCount = nMaxCount; //CDialog::OnCancel(); void CExam11_1Dlg::OnTimer(UINT nIDEvent) // TODO: Add your message handler code here and/or call default CProgressCtrl *pBar = (CProgressCtrl *)GetDlgItem(IDC_PROGRESS); pBar->SetPos(m_nCount * 100 / nMaxCount); CDialog::OnTimer(nIDEvent);
53
多執行緒程式設計 Why Multi-threading?
並不是每個程式都需要寫成多執行緒程式,因為當我們建立一個多執行緒程式時,不論在除錯以及撰寫難度上,都會提高很多,因為撰寫多執行緒程式就好像駕馭一台多頭馬車,控制不好馬車就可能會被拆散。 因為多執行緒的觀念,提供了程式設計師一個可以維持使用者介面不凍結,而又能同時處理瑣碎工作的機會。 多執行緒程式的意義,在於我們可以把一些工作,分配給其他 thread 執行,讓應用程式不會為了執行這些工作而造成整個視窗介面遭到凍結,致使無法回應使用者的訊息。
54
多執行緒程式設計 (續) 執行緒的種類 大體而言,一個行程的所有程式碼和資料空間對於該行程中的所有執行緒都是可用的。例如兩個執行緒都能夠存取相同的全域變數。 執行緒是由作業系統管理,每個執行緒都有自己的堆疊。 Windows 提供兩種執行緒:工作者執行緒 (worker threads) 和使用者介面執行緒 (UI threads) 。 UI 執行緒擁有視窗,所以它有自己的訊息迴路。 工作者執行緒沒有視窗,所以不需要處理訊息。工作者執行緒程式比較容易撰寫,也比較常用。
55
CWinThread
68
Resources IDR_THREAD IDM_PLAY
71
MainFrm.h ... class CMainFrame : public CFrameWnd {
protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar; CChildView m_wndView; CToolBar m_threadToolBar; };
72
MainFrm.cpp int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
... SetTimer(1, 100, NULL); m_threadToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_threadToolBar.LoadToolBar(IDR_THREAD); m_threadToolBar.EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_threadToolBar); return 0; } void CMainFrame::OnTimer(UINT nIDEvent) // TODO: Add your message handler code here and/or call default Invalidate(); CFrameWnd::OnTimer(nIDEvent);
73
ChildView.h ... #define WM_BBSORTSTOP (WM_USER + 10)
#define WM_QSORTSTOP (WM_USER + 11) struct THREAD_INFO { int *array; CWnd *wnd; } ; ///////////////////////////////////////////////////////////////////////////// // CChildView window class CChildView : public CWnd { // Construction public: CChildView(); // Attributes int m_BBSortArray[30]; int m_QSortArray[30]; CWinThread *m_BBSort, *m_QSort; THREAD_INFO if_BBSort, if_QSort;
74
ChildView.h // Operations public:
static UINT ThreadBBSort(LPVOID pParam); static UINT ThreadQSort(LPVOID pParam); static int qsort_partition(int *, int, int); static void qsort_sort(int *, int, int); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CChildView) protected: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //}}AFX_VIRTUAL // Implementation virtual ~CChildView(); // Generated message map functions //{{AFX_MSG(CChildView) afx_msg void OnPaint(); afx_msg void OnPlay(); afx_msg void OnUpdatePlay(CCmdUI* pCmdUI); //}}AFX_MSG afx_msg LRESULT OnBBSortStop(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnQSortStop(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() };
75
ChildView.cpp // ChildView.cpp : implementation of the CChildView class // ... CChildView::CChildView() { int i; CTime t = CTime::GetCurrentTime(); for(i = 0; i < 30; i++) m_BBSortArray[i] = m_QSortArray[i] = 20; m_BBSort = NULL; m_QSort = NULL; srand((unsigned)t.GetTime()); } BEGIN_MESSAGE_MAP(CChildView,CWnd ) //{{AFX_MSG_MAP(CChildView) ON_WM_PAINT() ON_COMMAND(IDM_PLAY, OnPlay) ON_UPDATE_COMMAND_UI(IDM_PLAY, OnUpdatePlay) //}}AFX_MSG_MAP ON_MESSAGE(WM_BBSORTSTOP, OnBBSortStop) ON_MESSAGE(WM_QSORTSTOP, OnQSortStop) END_MESSAGE_MAP()
76
ChildView.cpp (續) void CChildView::OnPaint() {
CPaintDC dc(this); // device context for painting CBrush bbrush(RGB(0, 0, 255)), wbrush(RGB(255, 255, 255)), rbrush(RGB(255, 0, 0)); int i; // TODO: Add your message handler code here dc.FillRect(CRect(0, 0, 1023, 767), &wbrush); if(m_BBSort) dc.FillRect(CRect(16, 16, 19, 19), &rbrush); for(i = 0; i < 30; i++) { dc.FillRect(CRect(20 + 1, * i + 1, 25 + m_BBSortArray[i] * 5 - 1, * i + 4), &bbrush); } if(m_QSort) dc.FillRect(CRect(16, 216, 19, 219), &rbrush); dc.FillRect(CRect(20 + 1, * i + 1, 25 + m_QSortArray[i] * 5 - 1, * i + 4), &bbrush); // Do not call CWnd::OnPaint() for painting messages void CChildView::OnPlay() // TODO: Add your command handler code here
77
ChildView.cpp (續) for(i = 0; i < 30; i++)
m_BBSortArray[i] = m_QSortArray[i] = 1 + rand() % 140; if_BBSort.array = m_BBSortArray; if_BBSort.wnd = this; m_BBSort = AfxBeginThread(ThreadBBSort, (LPVOID) &if_BBSort); if_QSort.array = m_QSortArray; if_QSort.wnd = this; m_QSort = AfxBeginThread(ThreadQSort, (LPVOID) &if_QSort); } UINT CChildView::ThreadBBSort(LPVOID pParam) { int i, j, t; int *a = ((THREAD_INFO *)pParam)->array; CWnd *wnd = ((THREAD_INFO *)pParam)->wnd; for(i = 0; i < 30; i++) { for(j = 28; j >= i; j--) { if(a[j] > a[j+1]) { t = a[j+1]; a[j+1] = a[j]; a[j] = t; volatile x1, x2; for(x1 = 0; x1 < 1000; x1++) { for(x2 = 0; x2 < 1000; x2++) { }
78
ChildView.cpp (續) } wnd->PostMessage(WM_BBSORTSTOP, 0, 0);
return 0; UINT CChildView::ThreadQSort(LPVOID pParam) { // int i, j, t; int *a = ((THREAD_INFO *)pParam)->array; CWnd *wnd = ((THREAD_INFO *)pParam)->wnd; qsort_sort(a, 0, 29); wnd->PostMessage(WM_QSORTSTOP, 0, 0); int CChildView::qsort_partition(int *a, int p, int r) { int x, i, j, t; x = a[p]; i = p - 1; j = r + 1; while(1) { do j = j - 1; while(!(a[j] <= x)); do i = i + 1; while(!(a[i] >= x)); if (i < j) { t = a[i]; a[i] = a[j];
79
ChildView.cpp (續) a[j] = t; volatile x1, x2;
for(x1 = 0; x1 < 1000; x1++) { for(x2 = 0; x2 < 1000; x2++) { } } } else { break; return j; void CChildView::qsort_sort(int *a, int p, int r) { int q; if(p < r) { q = qsort_partition(a, p, r); qsort_sort(a, p, q); qsort_sort(a, q+1, r);
80
ChildView.cpp (續) afx_msg LRESULT CChildView::OnBBSortStop(WPARAM wParam, LPARAM lParam) { // m_BBSort->SuspendThread(); // delete m_BBSort; m_BBSort = NULL; return 0; } afx_msg LRESULT CChildView::OnQSortStop(WPARAM wParam, LPARAM lParam) { // m_QSort->SuspendThread(); // delete m_QSort; m_QSort = NULL;
Similar presentations