Presentation is loading. Please wait.

Presentation is loading. Please wait.

Visual C++ Windows Programming

Similar presentations


Presentation on theme: "Visual C++ Windows Programming"— Presentation transcript:

1 Visual C++ Windows Programming
第十一章 訊息處理與多執行緒

2 大綱 Windows 訊息處理 多執行緒程式設計

3 Windows 訊息處理 Processes vs. Threads
當我們將一個應用程式載入到記憶體中執行時,該應用程式在記憶體中執行的狀態,一般而言我們統稱之為 process 。更精確的說法是,當我們將應用由硬碟載入記憶體時,系統分配給該應用程式的記憶體空間,程式碼,以及其他系統資源,這些資源的所有權我們將之視為一個 process 。當系統執行了不只一個 process 時,電腦將以 process 做為電腦分配資源的單位。 Process 是系統分配資源的單位,但是 CPU 時間的分配並非依照 process 來進行。真正分配 CPU 時間的基本單位是 thread 。 在每一個 process 裡,一開始只有一個 thread ,此 thread 稱為 primary thread 。而每一個 thread 還可以產生其他的 thread ,前者為 parent thread ,其他則為 child thread 。

4 Windows 訊息處理 (續) Processes vs. Threads (續)
Windows 所號稱的多工環境,基本上並不是真正的多工,因為系統實際上只有一個或者固定數目的 CPU 。在單 CPU 的系統中,所謂的多工是靠一個 CPU 搭配一個分配 CPU 時間的計時器,將 CPU 的計算時間分配給系統中執行的 thread ,由於再 thread 之間切換執行,分配 CPU 時間的速度很快,所以讓人感覺好像每個 thread 同時被執行,以達到多工的「效果」。 當 process 由一個以上的 threads 所組成時,我們稱這個 process 為多執行緒 (multi-threading) 程式。

5 Windows 訊息處理 (續) 單執行緒 (single-threading) 程式的訊息處理
在典型的 Windows 程式中,主執行緒會具備一個訊息處理迴圈的執行片段 (參考第一章的範例) ,藉由訊息偵測函式 GetMessage() 決定是否有訊息被處理。若沒有任何訊息要處理,則程式會被暫停 (suspend) ,讓其他程式執行。 倘若有訊息要被處理,則會接著執行 TranslateMessage() 函式,將鍵盤訊息轉換為輸入訊息之後,接著執行 DispatchMessage() 函式。這個函式會呼叫程式中的訊息處理函式,而訊息處理函式則會依據訊息的不同加以處理。處理完之後, DispatchMessage() 才會返回。 在 MFC 的架構中, MFC 會幫我們架構訊息處理函式,因此我們只要在訊息映射表中適當地填空,MFC 就會幫我們根據訊息呼叫適當的函式。

6 Windows 訊息處理 (續) 單執行緒程式的控制權釋放
假定我們的程式中有一個會持續耗用相當大的 CPU 資源的計算 (例如求一個 1000x1000 大小矩陣的反矩陣,或者是求 RSA-2048 密碼系統某明文的對應密文) 。在過去的 Windows 3.1 的時代,這段計算可能使得電腦停止回應﹔而即使是今日的 32-bit Windows 系統,這段計算仍然可能使得我們的程式停止回應。 為了避免我們的電腦或程式停止回應,我們可以在這個複雜計算的核心加入一小段與我們的訊息迴圈有類似行為的一段敘述。 PeekMessage() 函式運作方式和 GetMessage() 相當類似,但是它只會檢查該瞬間程式的訊息佇列 (message queue) 中是否有任何訊息,若有訊息則將訊息傳回,而沒有訊息的時候也會立即返回並告知程式無訊息。

7 Windows 訊息處理 (續) 計時器在單執行緒程式中的多工效果
Windows 的計時器 (timer) 功能能夠讓程式每隔固定時間就收到訊息。我們可以藉由處理計時器的函式,使得我們看起來具有多工的效果。 計時器的使用方法相當簡單,不論是直接使用 SDK 或者在 MFC 中。在 MFC 中,我們只要呼叫 CWnd 的成員函式 SetTimer() ,並且利用 ClassWizard 的幫助建立處理WM_TIMER 訊息的函式。一旦你啟動計時器時, WM_TIMER 訊息會不斷地送到你的視窗中,直到你呼叫 CWnd::KillTimer() 函式,或者計時器的視窗被 destroy 為止。 必要時,你可以使用數個計時器,並以整數值識別之。要注意的是,因為 Windows 並非即時作業系統,若你指定的時間間隔小於 100ms ,可能會不夠精確。

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23 *Dlg.h ... class CExam11_1Dlg : public CDialog { // Implementation
private: int m_nTimer; int m_nCount; enum { nMaxCount = }; };

24 *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++) {

25 *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; 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);

26 多執行緒程式設計 Why Multi-threading?
並不是每個程式都需要寫成多執行緒程式,因為當我們建立一個多執行緒程式時,不論在除錯以及撰寫難度上,都會提高很多,因為撰寫多執行緒程式就好像駕馭一台多頭馬車,控制不好馬車就可能會被拆散。 因為多執行緒的觀念,提供了程式設計師一調可以維持使用者介面不凍結,而又能同時處理瑣碎工作的機會。 多執行緒程式的意義,在於我們可以把一些工作,分配給其他 thread 執行,讓應用程式不會為了執行這些工作而造成整個視窗介面遭到凍結,致使無法回應使用者的訊息。

27 多執行緒程式設計 (續) 執行緒的種類 大體而言,一個行程的所有程式碼和資料空間對於該行程中的所有執行緒都是可用的。例如兩個執行緒都能夠存取相同的全域變數。 執行緒是由作業系統管理,每個執行緒都有自己的堆疊。 Windows 提供兩種執行緒:工作者執行緒 (worker threads) 和使用者介面執行緒 (UI threads) 。 UI 執行緒擁有視窗,所以它有自己的訊息迴路。 工作者執行緒沒有視窗,所以不需要處理訊息。工作者執行緒程式比較容易撰寫,也比較常用。

28 CWinThread

29

30

31

32

33

34

35

36

37

38

39

40

41 Resources IDR_THREAD IDM_PLAY

42

43

44 MainFrm.h ... class CMainFrame : public CFrameWnd {
protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar; CChildView m_wndView; CToolBar m_threadToolBar; };

45 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);

46 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;

47 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() };

48 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()

49 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

50 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++) { }

51 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];

52 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);

53 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;


Download ppt "Visual C++ Windows Programming"

Similar presentations


Ads by Google