C++多線程技術--API

1. windows API相關常用的線程函數

更多詳見MSDN--Process and Thread Functions

1.1. CreateThread

    CreateThread將在主線程的基礎上創建一個新線程,大致做如下步驟:
    1)在內核對象中分配一個線程標識/句柄,可供管理,由CreateThread返回
    2)把線程退出碼置爲STILL_ACTIVE,把線程掛起計數置1
    3)分配context結構
    4)分配兩頁的物理存儲以準備棧,保護頁設置爲PAGE_READWRITE,第2頁設爲PAGE_GUARD
    5)lpStartAddr和lpvThread值被放在棧頂,使它們成爲傳送給StartOfThread的參數
    6)把context結構的棧指針指向棧頂(第5步)指令指針指向startOfThread函數

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

    1)lpThreadAttributes:指向SECURITY_ATTRIBUTES型態的結構的指針。在Windows 98中忽略該參數。在Windows NT中,它被設爲NULL,表示使用缺省值。
    2)dwStackSize,線程堆棧大小,一般=0,在任何情況下,Windows根據需要動態延長堆棧的大小。   
    3)lpStartAddress,指向線程函數的指針,形式:@函數名,函數名稱沒有限制,但是必須以下列形式聲明:   DWORD WINAPI ThreadProc (LPVOID lpParam) ,格式不正確將無法調用成功。   
    4)lpParameter:向線程函數傳遞的參數,是一個指向結構的指針,不需傳遞參數時,爲NULL。   
    5)dwCreationFlags :線程標誌,可取值如下 :
  (a)CREATE_SUSPENDED-----創建一個掛起的線程,
  (b)0---------------------------------表示創建後立即激活。   
    6)lpThreadId:保存新線程的id。   
    7)返回值:函數成功,返回線程句柄;函數失敗返回false。 
    一般並不推薦使用 CreateThread函數,而推薦使用RTL 庫裏的System單元中定義的 BeginThread函數,因爲這除了能創建一個線程和一個入口函數以外,還增加了幾項保護措施。 
    在MFC程序中,應該調用AfxBeginThread函數,在Visual C++程序中應調用_beginthreadex函數。

1.2. CreateRemoteThread 與CreateRemoteThreadEx

    創建一個在其它進程地址空間中運行的線程(也稱:創建遠程線程)。

HANDLE WINAPI CreateRemoteThread( 
    __in HANDLE hProcess, 
    __in LPSECURITY_ATTRIBUTES lpThreadAttributes, 
    __in SIZE_T dwStackSize, 
    __in LPTHREAD_START_ROUTINE lpStartAddress, 
    __in LPVOID lpParameter, 
    __in DWORD dwCreationFlags, 
    __out LPDWORD lpThreadId 
);
HANDLE CreateRemoteThreadEx(
  __in       HANDLE hProcess,
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __in_opt   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
  __out_opt  LPDWORD lpThreadId
);

    1)hProcess:線程所屬進程的進程句柄. 該進程必須具有 PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ 訪問權限.
    2)lpThreadAttributes:一個指向 SECURITY_ATTRIBUTES 結構的指針, 該結指定了線程的安全屬性。可爲NULL,表示線程擁有默認的安全屬性且線程句柄不可被繼承。
    3)dwStackSize:線程初始大小,以字節爲單位,如果該值設爲0,那麼使用系統默認大小。
    4)lpStartAddress:在遠程進程的地址空間中,該線程的線程函數的起始地址
    5)lpParameter:傳給線程函數的參數
    6)dwCreationFlags:線程的創建標誌,表示創建後立即執行線程(0),還是將線程掛起(CREATE_SUSPENDED
    7)lpThreadId:輸出線程ID
    8)lpAttributeList:新線程的附加屬性,通過InitializeProcThreadAttributeList創建
    9)返回值:如果執行成功,返回新線程句柄以及輸出線程ID,否則返回NULL

1.3. ExitThread與TerminateThread

    用於非正常終止線程

1.3.1 ExitThread

  該函數將終止線程的運行,並導致操作系統消除該線程使用的所有操作系統資源,但是C++資源(如C++類對象堆內存)將不被撤銷。可以使用ExitThread的dwExitCode參數告知系統將該線程的退出碼設置爲該值。

1.3.2 TerminateThread

    在線程外終止一個線程,用於強制終止線程。

BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);

    1)hThread:被終止的線程的句柄
    2)dwExitCode:設置的線程退出碼
    3)返回值:函數執行成功則返回非零值,執行失敗返回0。
注意:
    使用此函數時,目標線程不再有機會執行任何用戶代碼,線程中加載的dll不會收到線程終止的通知,由系統釋放線程的初始化棧。
    TerminateThread是一個危險的函數,僅用於極端的情況。只有當你精確知道線程在做什麼且在線程終止時你能控制線程將要執行的所有代碼時,纔可使用該函數。
    使用

1.4. SuspendThread與ResumeThread

    掛起線程和執行掛起線程

1.5. GetExitCodeThread

    獲取線程退出碼

 

2. 使用C運行庫和Win32API

    所有 Win32 程序都至少有一個線程。任何線程都可以創建附加線程。線程可以快速完成其工作,然後終止;也可以在程序的生存期內保持活動狀態。
    LIBCMT 和 MSVCRT C 運行庫提供以下用於創建和終止線程的函數:_beginthread, _beginthreadex 和 _endthread, _endthreadex。
    _beginthread 和 _beginthreadex 函數創建新線程;如果操作成功,則返回線程標識符。線程完成執行時自動終止,或者可通過調用 _endthread 或 _endthreadex 自行終止。
    注意:如果要從使用 Libcmt.lib 生成的程序調用 C 運行時例程,則必須使用 _beginthread 或 _beginthreadex 函數啓動線程。不要使用 Win32 函數 ExitThread 和 CreateThread。如果有一個以上的線程在等待掛起的線程完成它對 C 運行時的數據結構的訪問時被阻塞,使用 SuspendThread 會導致死鎖。

2.1. _beginthread 和 _beginthreadex 函數

    _beginthread 和 _beginthreadex 函數用來創建新線程。線程與進程中的其他線程共享進程的代碼和數據段,但是線程具有自己的唯一寄存器值、堆棧空間和當前指令地址。系統給予每個線程 CPU 時間,使進程中的所有線程都可以同時執行。
    _beginthread 和 _beginthreadex 與 Win32 API 中的 CreateThread 函數類似,但有如下差異:
    它們初始化某些 C 運行庫變量。只有在線程中使用 C 運行庫時,這一點才很重要。
    CreateThread 幫助提供對安全特性的控制。可以使用此函數啓動處於掛起狀態的線程。
    如果成功的話,_beginthread 和 _beginthreadex 返回新線程的句柄;如果有錯誤的話,則返回錯誤代碼。

2.2. _endthread 和 _endthreadex 函數

    _endthread 函數終止由 _beginthread 創建的線程(同樣,_endthreadex 終止由 _beginthreadex 創建的線程)。線程會在完成時自動終止。_endthread 和 _endthreadex 用於從線程內部進行條件終止。例如,專門用於通信處理的線程若無法獲取對通信端口的控制,則會退出。

3.使用C++和MFC

    MFC 應用程序中的所有線程都由 CWinThread 對象表示。大多數情況下,甚至不必顯式創建這些對象,而只需調用框架 Helper 函數 AfxBeginThread,該函數將爲您創建 CWinThread 對象。
    MFC區分兩種類型的線程:用戶界面線程和輔助線程。用戶界面線程通常用於處理用戶輸入及響應用戶生成的事件和消息。輔助線程通常用於完成不需要用戶輸入的任務(如重新計算)。Win32 API 不區分線程類型;它只需要瞭解線程的起始地址以開始執行線程。MFC 爲用戶界面中的事件提供消息泵,從而對用戶界面線程進行專門處理。CWinApp 是用戶界面線程對象的一個示例,因爲它從 CWinThread 派生並對用戶生成的事件和消息進行處理。

3.1. 啓動線程

    AfxBeginThread 有兩個重載版本:一個用於用戶界面線程,一個用於輔助線程。若要開始執行輔助線程,請調用 AfxBeginThread,並提供下列信息:
    1)控制函數的地址。
    2)要傳遞到控制函數的參數。
    3)(可選)所需的線程優先級。默認值爲正常優先級。有關可用的優先級級別的更多信息,請參見 Platform SDK 中的 SetThreadPriority。
    4)(可選)所需的線程堆棧大小。默認值與創建線程的堆棧大小相同。
    5)(可選)CREATE_SUSPENDED,如果希望在掛起狀態中創建線程。默認值爲 0,即正常啓動線程。
    6)(可選)所需的安全屬性。默認值與父線程具有相同的訪問權。有關此安全信息格式的更多信息,請參見 Platform SDK 中的 SECURITY_ATTRIBUTES。
    AfxBeginThread 爲您創建和初始化 CWinThread 對象、啓動該對象並返回其地址,以便以後引用。在整個過程中進行檢查,確保假如創建過程的任何部分出現故障,所有對象都能被正確地解除分配。

3.2. 終止線程

3.2.1 正常線程終止

    對於輔助線程,正常線程終止很簡單:退出控制函數並返回表示終止原因的值。可以使用 AfxEndThread 函數或 return 語句。一般情況下,0 表示成功完成,但這取決於您自己。
    對於用戶界面線程,該過程也很簡單:從用戶界面線程內調用 Platform SDK 中的 PostQuitMessage。PostQuitMessage 採用的唯一參數是線程的退出代碼。對於輔助線程,0 通常表示成功完成。

3.2.2 過早的線程終止

    過早終止線程幾乎一樣簡單:從線程內調用 AfxEndThread。將所需的退出代碼作爲唯一參數傳遞。這將停止執行線程、解除對線程堆棧的分配、分離附加到線程的所有 DLL 並從內存中刪除線程對象。
    必須從要終止的線程內調用 AfxEndThread。如果要從其他線程終止線程,必須設置兩個線程間的通信方法。

3.2.3 檢索線程的退出代碼

    若要獲取輔助線程或用戶界面線程的退出代碼,請調用 GetExitCodeThread 函數。此函數獲取線程(存儲在 CWinThread 對象的 m_hThread 數據成員中)的句柄和 DWORD 的地址。
    如果線程仍然是活動的,GetExitCodeThread 將 STILL_ACTIVE 放置在提供的 DWORD 地址中;否則將退出代碼放置在該地址中。
    檢索 CWinThread 對象的退出代碼還需要一步。默認情況下,當 CWinThread 線程終止時,刪除該線程對象。這意味着不能訪問 m_hThread 數據成員,因爲 CWinThread 對象不再存在。若要避免出現這種情況,請執行以下操作之一:
    1)將 m_bAutoDelete 數據成員設置爲 FALSE。這使 CWinThread 對象在線程終止後仍可以繼續存在。然後可以在線程終止後,訪問 m_hThread 數據成員。但是,如果使用此方法,就得銷燬 CWinThread 對象,因爲框架不會自動刪除該對象。這是首選方法。
    2)單獨存儲線程的句柄。創建線程後,(使用 ::DuplicateHandle)將其 m_hThread 數據成員複製到其他變量,並通過該變量訪問該成員。這樣,終止後即會自動刪除對象,並且仍然可以找到線程終止的原因。請注意:在可以複製句柄之前,線程不終止。執行此操作的最安全的方式是將 CREATE_SUSPENDED 傳遞到 AfxBeginThread,存儲句柄,然後通過調用 ResumeThread 繼續執行線程。
    任一方法都可以使您確定 CWinThread 對象終止的原因。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章