天天看點

多線程程式設計複習筆記 線程的建立

方式一:

CreateThread

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE  lpStartAddress,

 LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId

);

lpThreadAttributes指向SECURITY_ATTRIBUTES型态的結構的指針,這個參數通常設定為NULL

dwStackSize設定初始棧的大小,以位元組為機關,如果為0,那麼預設将使用與調用該函數的線程相同的棧空間大小。任何情況下,Windows根據需要動态延長堆棧的大小

lpStartAddress, 指向線程函數的指針,函數名稱沒有限制,但是必須以下列形式聲明:

DWORD WINAPI ThreadProc (LPVOID lpParam) ,格式不正确将無法調用成功

lpParameter, 向線程函數傳遞的參數,是一個指向結構的指針,不需傳遞參數時,為NULL

dwCreationFlags, 線程标志,可取值如下

(1)CREATE_SUSPENDED(0x00000004):建立一個挂起的線程,

(2)0:表示建立後立即激活。

lpThreadId  lpThreadId:儲存新線程的id。

調用CreateThread時,系統會建立一個線程核心對象,這個線程核心對象不是線程本身,而是一個較小的資料結構,操

作系統用這個結構來管理線程,可以把線程核心對象想象為一個由線程統計資訊構成的小型資料結構。

定義線程函數:

static     DWORD WINAPI ThreadProc (LPVOID lpParam) ; //一般用靜态函數 

由于線程函數是靜态函數,如果要在函數中用到對象,必須通過函數的實作 

方式二:

_beginthreadex

CreateThread()函數是Windows提供的API接口,在C/C++語言另有一個建立線程的函數_beginthreadex(),在很多書上(包括《Windows核心程式設計》)提到過盡量使用_beginthreadex()來代替使用CreateThread()

原因:

标準C運作庫在1970年被實作了,由于當時沒任何一個作業系統提供對多線程的支援。是以編寫标準C運作庫的程式員根本沒考慮多線程程式使用标準C運作庫的情況.标準C運作庫的全局變量errno。很多運作庫中的函數在出錯時會将錯誤代号指派給這個全局變量,這樣可以友善調試。但如果有這樣的一個代碼片段:

  1. if (system("notepad.exe readme.txt") == -1)  
  2. {  
  3.     switch(errno)  
  4.     {  
  5.         ...//錯誤處理代碼  
  6.     }  
  7. }  

假設某個線程A在執行上面的代碼,該線程在調用system()之後且尚未調用switch()語句時另外一個線程B啟動了,這個線程B也調用了标準C運作庫的函數,不幸的是這個函數執行出錯了并将錯誤代号寫入全局變量errno中。這樣線程A一旦開始執行switch()語句時,它将通路一個被B線程改動了的errno。這種情況必須要加以避免!因為不單單是這一個變量會出問題,其它像strerror()、strtok()、tmpnam()、gmtime()、asctime()等函數也會遇到這種由多個線程通路修改導緻的資料覆寫問題。

為了解決這個問題,Windows作業系統提供了這樣的一種解決方案——每個線程都将擁有自己專用的一塊記憶體區域來供标準C運作庫中所有有需要的函數使用。而且這塊記憶體區域的建立就是由C/C++運作庫函數_beginthreadex()來負責的

總結來說: 

_beginthreadex函數就是為了标準C運作庫而設計的函數,可以在使用C運作庫函數的時候達到“線程同步”的效果!

方式三:AfxBeginThread

使用者界面線程和工作者線程都是由AfxBeginThread建立的。現在,考察該函數:MFC提供了兩個重載版的AfxBeginThread,一個用于使用者界面線程,另一個用于工作者線程,分别有如下的原型和過程

使用者界面線程的AfxBeginThread

使用者界面線程的AfxBeginThread的原型如下: CWinThread* AFXAPI AfxBeginThread(

  CRuntimeClass* pThreadClass,

  int nPriority,

  UINT nStackSize,

  DWORD dwCreateFlags,

  LPSECURITY_ATTRIBUTES lpSecurityAttrs) 其中: 參數1是從CWinThread派生的RUNTIME_CLASS類; 參數2指定線程優先級,如果為0,則與建立該線程的線程相同; 參數3指定線程的 堆棧大小,如果為0,則與建立該線程的線程相同; 參數4是一個建立辨別,如果是CREATE_SUSPENDED,則在懸挂狀态建立 線程,線上程建立後線程挂起,否則線程在建立後開始線程的執行。 參數5表示線程的安全屬性,NT下有用。

工作者線程的AfxBeginThread

工作者線程的AfxBeginThread的原型如下: CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,

  LPVOID lParam,

  int nPriority = THREAD_PRIORITY_NORMAL,

  UINT nStackSize = 0,

  DWORD dwCreateFlags = 0,

  LP SECURITY_ATTRIBUTES  lpSecurityAttrs = NULL

  );//用于建立工作者線程 傳回值: 成功時傳回一個指向新線程的線程對象的 指針,否則NULL。 pfnThreadProc : 線程的入口函數,聲明一定要如下: UINT MyThreadFunction(LPVOID pParam),不能設定為NULL; pParam : 傳遞入線程的參數,注意它的類型為:LPVOID,是以我們可以傳遞一個 結構體入線程. nPriority : 線程的優先級,一般設定為 0 .讓它和 主線程具有共同的優先級. nStackSize : 指定新建立的線程的棧的大小.如果為 0,新建立的線程具有和 主線程一樣的大小的棧 dwCreateFlags : 指定建立線程以後,線程有怎麼樣的标志.可以指定兩個值: CREATE_SUSPENDED : 線程建立以後,會處于 挂起狀态,直到調用:ResumeThread 0 : 建立線程後就開始運作. lpSecurityAttrs : 指向一個 SECURITY_ATTRIBUTES 的 結構體,用它來标志新建立線程的安全性.如果為 NULL, 那麼新建立的線程就具有和 主線程一樣的安全性. 如果要在 線程内結束線程,可以線上程内調用   AfxEndThread. 結束線程的兩種方式 當你在 背景用線程來列印一些圖形時.有時在列印一部分後,你希望可以停下來,那麼此如何讓線程停止呢.

繼續閱讀