程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> 例程分析多線程編程

例程分析多線程編程

編輯:vc教程
   Windows系統平台經歷了16位到32位的轉變後,系統運行方式和任務管理方式有了很大的變化,在Windows 95和Windows NT中,每個Win32程序在獨立的進程空間上運行,32位地址空間使我們從16位段式結構的64K段限制中擺脫出來,邏輯上達到了4G的線性地址空間,我們在設計程序時,不再需要考慮編譯的段模式,同時還提高了大程序的運行效率。獨立進程空間的另一個更大的優越性是大大提高了系統的穩定性,一個應用的異常錯誤不會影響其它的應用。與在MS-DOS和16位Windows操作系統中不同,32位Windows進程是沒有活力的。這就是說,一個32位Windows進程並不執行什麼指令,它只是占據著4GB的地址空間,此空間中有應用程序EXE文件的代碼和數據。EXE需要的DLL也將它們的代碼的數據裝入到進程的地址空間。除了地址空間,進程還占有某些資源,比如文件、動態內存分配和線程。當進程終止時,在它生命期中創建的各種資源將被清除。

  如上所述,進程是沒有活力的,它只是一個靜態的概念。為了讓進程完成一些工作,進程必須至少占有一線程,所以線程是描述進程內的執行,正是線程負責執行包含在進程的地址空間中的代碼。實際上,單個進程可能包含幾個線程,它們可以同時執行進程的地址空間中的代碼。為了做到這一點,每個線程有自己的一組CPU寄存器和椎。每個進程至少有一個線址程在執行其地址空間中的代碼,如果沒有線程執行進程地空間中的代碼,如果沒有線程執行進程地址空間中的代碼,進程也就沒有繼續存在的理由,系統將自動清除進程及其地址空間。為了運行所有這些線程,操作系統為每個獨立線程安排一些CPU時間,操作系統以輪轉方式向線程提供時間片,這就給人一種假象,好象這些線程都在同時運行。創建一個32位Windows進程時,它的第一個線程稱為主線程,由系統自動生成,然後可由這個主線程生成額外的線程,這些線程又可生成更多的線程。

  例如,在基於Internet網上的可視電話系統中,同時要進行語音采集、語音編譯碼、圖像采集、圖像編譯碼、語音和圖像碼流的傳輸,所有這些工作,都要並行處理。特別是語音信號,如果進行圖像編解碼時間過長,語音信號得不到服務,通話就有間斷;如果圖像或語音處理時間過長,而不能及時傳輸碼流數據,通信同樣也會中斷。這樣就要求我們實現一種並行編程,在只有一個CPU的機器上,也就是要將該CPU時間按時一定的優先准則分配給各個事件,定期處理各事件,而不會對某一事件處理過長。

  在32位Windows95或Windows NT下,我們可以用多線程的處理技術來實現這種並行處理。實際上,這種並行編程在很多場合下都是必須的。再例如,在File Manager拷貝文件時,它顯示一個對話框中包含了一個Cancel按鈕。如果在文件拷貝過程中,點中Cance l按鈕,就會終止拷貝。在16位Winows中,實現這類功能需要在File Copy循環內部周期性地調用PeekMessage函數。如果正在讀一個很大的動作;如果從軟盤讀文件,則要花費好幾秒的時間。由於機器反應太遲鈍,用戶會頻繁地點中這個按鈕,以為系統不知道想終止這個操作。如果把File Copy指令放入另外一個線程,就不需要在代碼中放一大堆PeekMessage函數,處理用戶界面的線程將與它分開操作,點中Cancel按鈕後會立即得到響應。同樣的道理,在應用程序中創建一個單獨線程來處理所有打印任務也是很有用的,用戶可以在打印處理時繼續使用應用程序。

  多線程的編程在Win32方式下和MFC類庫支持下的原理是一致的,進程的主線程在任何需要的時候都可以創建新的線程,當線程執行完任務後,自動終止線程,當進程結束後,所有的線程都終止。所有活動的線程共享進程的資源,所以在編程時,需要考慮在多個線程訪問同一資源時,產生沖突的問題,當一個線程正在訪問一個進程對象,這時另一個線程要改變該對象,這時可能會產生錯誤的結果,所以程序員在編程時要解決這種沖突。
下面舉例說明在Win32 基礎上進行多線程編程的過程。

  1.使用函數說明

  Win32函數庫裡提供了多線程控制的操作函數,包括創建線程、終止線程、建立互斥區等。首先,在應用程序的主線程或者其它活動線程的適當的地方創建新的線程,創建線程的函數如下:

  HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,

  DWord dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,

  LPVOID lpParameter, DWORD dwCreationFlags, LPDWord lpThreadId );

  參數lpThreadAttributes 指定了線程的安全屬性,在Windows 95中被忽略;

  參數dwStackSize 指定了線程的堆棧深度;

  參數lpStartAddress 指定了線程的起始地址,一般情況為下面原型的函數

  DWord WINAPI ThreadFunc( LPVOID );

  參數 lpParameter指定了線程執行時傳送給線程的32位參數,即上面函數的參數;

  參數dwCreationFlags指定了線程創建的特性;

  參數 lpThreadId 指向一個DWord變量,可返回線程ID值。

  如果創建成功則返回線程的句柄,否則返回NULL。

  創建了新的線程後,線程開始啟動執行,如果在dwCreationFlags中用了CREATE_SUSPENDED特性,那麼線程並不馬上執行,而是先掛起,等到調用ResumeThread後才開始啟動線程,在這過程中可以調用函數

  BOOL SetThreadPriority( HANDLE hThread, int nPriority);

  設置線程的優先權。

  當線程的函數返回後,線程自動終止,如果要想在線程的執行過程中終止的話,可以調用函數

  VOID ExitThread( DWord dwExitCode);

  如果在線程的外面終止線程的話,可以用下面的函數

  BOOL TerminateThread( HANDLE hThread, DWord dwExitCode );

  但注意,該函數可能會引起系統不穩定,而且,線程所占用的資源也不釋放,因此,一般情況下,建議不要使用該函數。

  如果要終止的線程是進程內的最後一個線程的話,在線程被終止後,相應的進程也終止。

  2. 無優先級例程,該例程實現在對話框上通過一線程實現時鐘的顯示和停止。步驟如下:

  第一步:建立一基於對話框的工程MultiProcess1。

  第二步:在對話框上建立兩個按鈕和一個編輯框,ID號分別為ID_START、ID_STOP和IDC_TIME,Caption分別為"啟動"、"停止"。如下:

  第三步:在MultiProcess1Dlg.cpp中定義全局函數ThreadProc(),格式如下:

void ThreadProc()
 {
  CTime time;
  CString m_time;
  for(;;)
   {
    time=CTime::GetCurrentTime();
    m_time=time.Format("%H:%M:%S");
    ::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_TIME,m_time);
    Sleep(1000);
   }
  }
 

  第四步:在頭文件MultiProcess1Dlg.h中定義變量如下:

   DWord ThreadID;

   HANDLE hThread;

  第五步:雙擊"開始"按鈕,生成消息映射函數OnStart(),編寫其中的代碼如下:

void CMultiProcess1Dlg::OnStart()
{
 // TODO: Add your control notification handler code here
 hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,
   NULL,0,&ThreadID);
}

  此時即刻實現在對話框上點擊"啟動",啟動時鐘。接下來我們實現如何讓時鐘停下來。

  第六步:雙擊"停止"按鈕,添加停止的消息映射函數OnStop(),編寫代碼如下:

void CMultiProcess1Dlg::OnStop()
{
// TODO: Add your control notification handler code here
TerminateThread(hThread,1);
}
 
  注意:該函數可能會引起系統不穩定,而且,線程所占用的資源也不釋放,因此,一般情況下,建議不要使用該函數。

  到現在,這個程序就完整了,看一下效果吧!

  最後需要說明的是,並不是設計多線程就是一個好的程序。目前大多數的計算機都是單處理器(CPU)的,在這種機器上運行多線程程序,有時反而會降低系統性能,如果兩個非常活躍的線程為了搶奪對CPU的控制權,在線程切換時會消耗很多的CPU資源,但對於大部分時間被阻塞的線程(例如等待文件 I/O 操作),可以用一個單獨的線程來完成,這樣可把CPU時間讓出來,使程序獲得更好的性能。因此,在設計多線程應用時,需要慎重選擇,具體情況具體處理,以使應用程序獲得最佳的性能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved