第三章--Win32程序的執行單元(部分概念及代碼講解)(上 -- 多線程)

學習《Windows程序設計》記錄編程

概念貼士:windows

1.  線程描述了進程內代碼的執行路徑。
數組

 

2.  _stdcall是新標準C/C++函數的調用方法。從底層來講,使用這種調用方法參數的進棧順序和標準C調用(_cdecl方法)是同樣的,可是_stdcall採用自動清棧的方式,而_cdecl採用的是手動清棧方式。瀏覽器

 

3.  Windows規定,凡是由其負責調用的函數一概定義爲_stdcall類型。ThreadProc是一個回調函數,即有Windows系統負責調用的函數,因此該函數應定義爲_stdcall類型。另外,當沒有顯式說明時,函數默認的調用方法是_cdecl。安全

 

4.  建立新線程的函數是CreateThread,由這個函數建立的線程將在調用者的虛擬地址空間內執行。該函數執行成功後,將返回新建線程的線程句柄。數據結構

 

5.  WaitForSingleObject函數用於等待指定的對象(hHandle)變成受信狀態。其中參數dwMilliseconds給出了以毫秒爲單位的要等待時間。當其值爲INFINITE時,表示要等待無限長時間。函數

  PS:  WaitForSingleObject(學習

                  hThread,    //要等待對象的句柄spa

                  INFINITE    //要等待的時間(以毫秒爲單位)線程

                );

 

6.  當發生如下狀況時,WaitForSingleObject函數就會返回:

    1)要等待的對象變成受信(signaled)狀態;

    2)參數dwMilliseconds指定的時間已過去了。

  PS:一個可執行對象有兩種狀態,未受信(nonsignaled)和受信(signaled)狀態。線程對象只有當線程運行結束時才達到受信狀態。

 

7.  當建立子進程時,若是爲CreateProcess函數的bInheritHandles參數傳遞TRUE,那麼子進程就能夠繼承父進程的可繼承句柄。

 

8.  dwCreationFlags--建立標誌。若是爲0,表示線程被建立後當即開始執行,若是指定爲CREATE_SUSPENDED標誌,表示線程被建立後處於掛起狀態,即暫停狀態,知道使用ResumeThread函數顯式地啓動該線程爲止。

 

9.  線程內核對象能夠說是一個包含了線程狀態信息的數據結構。系統提供的管理線程的函數其實就是依靠訪問線程內核對象來實現管理的。

     圖表:    CONTEXT(上下文,即寄存器的狀態)

                  EAX

                  EBX

                其餘CPU寄存器

            Usage Count  使用計數(2)

            Suspend Count  暫停次數(1)

            Exi Code  退出代碼(STILL_ACTIVE)

            Signaled  是否受信(FALSE)

            ...  ...  ...  ...

 

10.  線程上下文:每一個線程都有着他本身的一組CPU寄存器,稱爲線程的上下文。

 

11.  使用次數:Usage Count成員記錄了線程內核對象的使用次數,這個計數說明了此內核對象被打開的次數。只要線程沒有結束運行,那麼這個計數的值至少爲1。在建立一個新的線程是,CreateThread函數返回了線程內核對象的句柄,至關於打開一次新建立的內核對象,這也會促使Usage Count的值加1。因此建立一個新的進程後,初始狀態下Usage Count的值爲2。以後,只要有進程打開這個內核對象,就會使得Usage Count的值加1。因爲OpenThread函數的調用會靈Usage Count的值加1,因此在用完它們返回的句柄後必定要用CloseHandle函數進行關閉。(不關閉句柄,會形成內存泄漏。固然線程所在的進程結束後,該進程佔用的全部資源都會統一釋放。)線程函數一旦返回,線程的生命週期就到此爲止。

 

12.  暫停次數:線程內核對象中的Suspend Count用於指明線程的暫停次數。建立線程的時候指定CREATE_SUSPENDED標誌,就能夠在線程有機會在執行任何代碼以前改變線程的運行環境。(如優先級。)ResumeThread函數(喚醒一個被掛起的線程)會減小線程的暫停次數。注意,一個線程能夠被暫停若干次。若是一個線程被暫停了3次,它必須被喚醒3次才能夠分配給一個CPU。任何線程均可以調用SuspendThread函數來暫停另外一個線程的運行。該函數能夠增長線程的暫停次數。

 

13.  退出代碼:成員Exit Code指定了線程的退出代碼,也能夠說是線程函數的返回值。同時也能夠用GetExitCodeThread函數來獲取線程的退出代碼。

 

14.  是否受信:成員Signaled指定了線程對象是否爲「受信」狀態。線程在運行期間,Signaled的值永遠是FALSE,即「未受信」。只有當線程結束後,系統纔會把Signaled的值置爲TRUE。

 

15.  線程的終止:當線程正常終止時,會發生下列事件:

      1)在線程函數中建立的全部C++對象將經過它們各自的析構函數被正確地銷燬。

      2)該線程使用的堆棧將被釋放。

      3)系統將線程內核對象中Exit Code(退出代碼)的值由STELL_ACTIVE設置爲線程函數的返回值。

      4)系統將遞減線程內核對象中Usage Code(使用計數)的值。

 

16.  終止線程的執行有四種方法。

      1)線程函數天然退出。當函數執行到return語句返回時,Windows將終止線程的執行。建議使用這種方法終止線程的執行。

      2)使用ExitThread函數來終止線程。該函數會停止當前線程的進行,促使系統釋放全部該線程使用的資源,但C/C++資源卻不能獲得正確的清楚。這裏有一個有關析構函數的例子,就不展現了。

      3)使用TerminateThread函數在一個線程中強制終止另外一個線程的執行。注意,這是被強烈建議避免的函數。由於一旦執行這個函數,程序將沒法預測目標線程在何處被終止,這形成目標線程可能根本沒有機會來作清除工做,如打開文件佔用的內存等都不會被釋放。另外,使用該函數終止進程的時候,系統不會釋放線程使用的堆棧。

      4)使用ExitProcess函數來終止進程,系統會自動結束進程中全部線程的運行。可是這種方法至關於對每個線程使用了TerminateThread函數,因此一樣強烈建議避免。

      PS:每個線程都應該讓它正常退出,即由它的線程函數返回。通知線程退出有不少方法,如使用事件對象、設置全局變量等。

 

17.  線程的優先級:每一個線程都要賦予一個優先級號,取值從0(最低)到31(最高)。

 

18.  Windows支持6個優先級類:idle、below normal、normal、above normal、high和real-time。進程屬於一個優先級類,還能夠爲進程內的線程賦予一個相對線程優先級。線程剛被建立時,它的相對優先級老是被設置爲normal。(表示解壓文件時我修改了進程優先級,沒感受速度加快了啊。憂傷。)

 

19.  SetThreadPriority(HANDLE hThread, int nPriority)函數用去設置線程優先級。參數中前者是目標線程句柄,後者定義了線程的優先級。(後面會有相關優先級例子的展現:PriorityDemo.)

 

20.  實際編程中改變線程優先級的經常使用方法:建立一個線程的時候,將CREATE_SUSPENDED標記傳給了CreateThread函數,這可使得新線程處於暫停狀態。在將它的優先級設爲須要的優先級。再調用ResumeThread函數恢復線程的運行。

 

21.  WaitForMultipleObjects函數用於等待多個內核對象。(在實例PriorityDemo中最後有所展現。)

 

22.  Windows Explorer進程中的線程就是在高優先級下運行的。(請不要聯想到IE瀏覽器。這裏的Explorer是指Windows程序管理器或者文件資源管理器,能夠說是圖形界面的核心。別問我爲何知道這麼清楚,由於我關閉過這個進程。)

 

23.  在實際開發中,通常不直接使用Windows系統提供的CreateThread函數建立線程,而是使用C/C++運行期函數_beginthreadex。(注:VC默認的C/C++運行期庫並不支持_beginthreadex函數。囧。)相應地,C/C++運行期庫一樣提供了另外一個用於結束當前線程運行的函數--_endthreadex函數,用於取代ExitThread函數。

 

 

代碼解釋:

1.ThreadDemo

  PS:主線程首先建立了一個輔助線程,打印出輔助線程的ID號,而後等待輔助線程運行結束;輔助線程僅打印出幾行字符串,僅模擬真正的工做。

 

 1 #include <stdio.h>
 2 #include <windows.h>
 3 
 4 // 線程函數
 5 DWORD WINAPI ThreadProc(LPVOID lpParam)  6 {  7     int i = 0;  8     while(i < 20)  9  { 10         printf(" I am from a thread, count = %d \n", i++); 11  } 12     return 0; 13 } 14 
15 int  main(int argc, char* argv[]) 16 { 17  HANDLE hThread; 18  DWORD dwThreadId; 19     
20     // 建立一個線程
21     hThread = ::CreateThread ( 22         NULL,        // 默認安全屬性
23         NULL,        // 默認堆棧大小
24         ThreadProc,    // 線程入口地址(執行線程的函數)
25         NULL,        // 傳給函數的參數
26         0,        // 指定線程當即運行
27         &dwThreadId);    // 返回線程的ID號
28     printf(" Now another thread has been created. ID = %d \n", dwThreadId); 29 
30     // 等待新線程運行結束
31  ::WaitForSingleObject (hThread, INFINITE); 32  ::CloseHandle (hThread); 33     return 0; 34 }

 

 

2.PriorityDemo

  PS:下列程序同時建立了兩個線程,一個線程的優先級是「空閒」,運行的時候不斷打印出"Idle Thread is running"字符串;另外一個線程的優先級是「正常」,運行的時候不斷打印出「Normal Thread is running」字符創。

 1 #include <stdio.h>
 2 #include <windows.h>
 3 
 4 DWORD WINAPI ThreadIdle(LPVOID lpParam)  5 {  6     int i = 0;  7     while(i++<10)  8         printf("Idle Thread is running \n");  9 
10     return 0; 11 } 12 
13 DWORD WINAPI ThreadNormal(LPVOID lpParam) 14 { 15     int i = 0; 16     while(i++<10) 17         printf(" Normal Thread is running \n"); 18 
19     return 0; 20 } 21 int main(int argc, char* argv[]) 22 { 23  DWORD dwThreadID; 24     HANDLE h[2]; 25     
26     // 建立一個優先級爲Idle的線程
27     h[0] = ::CreateThread(NULL, 0, ThreadIdle, NULL, 28         CREATE_SUSPENDED, &dwThreadID); 29     ::SetThreadPriority(h[0], THREAD_PRIORITY_IDLE); 30     ::ResumeThread(h[0]); 31 
32     // 建立一個優先級爲Normal的線程
33     h[1] = ::CreateThread(NULL, 0, ThreadNormal, NULL, 34         0, &dwThreadID); 35     
36     // 等待兩個線程內核對象都變成受信狀態
37  ::WaitForMultipleObjects( 38         2,        // DWORD nCount 要等待的內核對象的數量
39         h,        // CONST HANDLE *lpHandles 句柄數組
40         TRUE,        // BOOL bWaitAll 指定是否等待全部內核對象變成受信狀態
41         INFINITE);  // DWORD dwMilliseconds 要等待的時間
42        
43     ::CloseHandle(h[0]); 44     ::CloseHandle(h[1]); 45 
46     return 0; 47 } 48 
49 /*
50  HANDLE h[2]; 51  h[0] = hThread1; 52  h[1] = hThread2; 53  DWORD dw = ::WaitForMultipleObjects(2, h, FALSE, 5000); 54  switch(dw) 55  { 56  case WAIT_FAILED: 57  // 調用WaitForMultipleObjects函數失敗(句柄無效?) 58  break; 59  case WAIT_TIMEOUT: 60  // 在5秒內沒有一個內核對象受信 61  break; 62  case WAIT_OBJECT_0 + 0: 63  // 句柄h[0]對應的內核對象受信 64  break; 65  case WAIT_OBJECT_0 + 1: 66  // 句柄h[1]對應的內核對象受信 67  break; 68  } 69 */
相關文章
相關標籤/搜索