Windows 核心編程之6 線程內核對象

線程內核對象
每個進程裏,都有一個線程,操作系統用線程內核對象來管理線程。

線程有2部分組成:1 線程內核對象。 操作系統來管理線程   2 線程棧, 來存在函數和局部變量的地址空間。


什麼情況下使用多線程?
     當各個任務彼此獨立時,可以使用多線程,如:開一個後臺線程來檢測語法。
什麼情況下不要使用多線程?
      任務聯繫緊密。如 掃描一個目錄的文件,就不適合開多個線程處理,這樣處理起來複雜度很高!

實現CreateThread 來創建一個線程.
如果是C、C++代碼的話,使用_beginThreadEx來創建線程
爲什麼? 因爲有些C函數裏面使用了靜態變量,那麼多個線程執行的話,會有衝突,但是_beginThreadex做了線程局部處理,它內部就調用了CreateThread函數。



WINBASEAPI   表示是winbase.h的API
__out_opt       表示是輸出
HANDLE
WINAPI
CreateThread(
    __in_opt  LPSECURITY_ATTRIBUTES lpThreadAttributes,   安全屬性,一般爲NULL表示默認
    __in      SIZE_T dwStackSize,    線程棧的空間,一般使用0來表示默認值
    __in      LPTHREAD_START_ROUTINE lpStartAddress,  線程函數地址
    __in_opt  LPVOID lpParameter, 線程函數參數
    __in      DWORD dwCreationFlags,  創建線程時,指定啥標誌 0 表示立即開始執行,CREATE_SUSPENDED 表示暫停
    __out_opt LPDWORD lpThreadId   返回線程ID,不需要時,可以傳遞NULL,表示不關心
    );


終止運行線程的4種方式:

1 線程函數返回(推薦

可以清理C++對象資源,會調用C++的析構函數
清理線程棧

2 自己調用ExitThread(避免使用

不會調用C++的析構函數
清理線程棧
如果使用的是C++代碼,那麼調用_endThreadEx() 來替換ExitThread

3 其它線程調用TerminateThread()  避免使用

不會調用C++析構函數,也不會清理線程棧
ExitThread來結束的線程,會清理堆棧,但是調用terminateThread()函數 不會清理堆棧信息,除非線程所在進程終止。


4 線程所在的進程終止                      避免使用
C++析構函數不會調用,線程被強行殺死,正常的清理工作沒有執行。



線程終止時,會發生以下事情:

1  線程擁有的所有對象句柄被釋放
一個線程有2個用戶對象: 窗口和掛鉤

2 退出碼有STILL_ACTIVE 變成 ExitThread 或者TerminateThread 返回的退出碼

3 線程內核對象變成觸發狀態

4 如果線程是最後一個活動線程,那麼進程也終止。

5 線程內核對象的引用計數減一


C和C++庫
標準的C和C++庫,最開始不是爲了多線程設計的,在多線程環境下有些函數會出問題:
errno,_doserrno, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile,asctime,gmtime,等

所以要不要調用系統的CreateThread,而要調用C,C++運行庫 _beginthreadex

_beginthreadex 分配和初始化 初始化數據塊,並將它與新的線程關聯起來
_endthreadex  函數 在線程終止運行的 釋放分配的數據塊

不要使用C和C++運行庫的這一對函數
_beginthread , _endthread

瞭解自己的身份

返回當前進程的句柄  和  ID
HANDLE GetCurrentProcess();
DWORD GetCurentProceddID();

返回當前線程的句柄   和 ID
HANDLE GetCurrentThread();
DWORD  GetCureentThreadID();


注意: 上面這2個函數返回的都是僞句柄

僞句柄:表示當前線程的句柄,即調用線程的句柄。下面舉一個示例來分析
#include <iostream>
#include <vector>
#include <map>
#include <Windows.h>

#include <tchar.h>
#include <process.h>
#include <Strsafe.h>

HANDLE ghthreadCurrent = NULL;
unsigned int WINAPI ThreadFunc2(LPVOID lp);

unsigned int WINAPI ThreadFunc1(LPVOID lp)
{
	HANDLE hThread = GetCurrentThread();

	ghthreadCurrent =(HANDLE) _beginthreadex(NULL, 0, ThreadFunc2, hThread, 0, NULL);

	while(1)
	{
		printf("111\n");
		Sleep(1000);
	}
	return 0;
}

unsigned int WINAPI ThreadFunc2(LPVOID lp)
{
	HANDLE hThread = (HANDLE)lp;

	TerminateThread(hThread,0);

	return 0;
}

int main()
{
	HANDLE hThread1 =(HANDLE) _beginthreadex(NULL, 0, ThreadFunc1, NULL, 0, NULL );

	WaitForSingleObject(hThread1,INFINITE);

	return 0;
}



線程2 的線程函數 TerminateThread 會把自己殺死,不會殺死線程1


使用一個複製句柄的函數 DuplicateHandle() 來複制句柄
#include <iostream>
#include <vector>
#include <map>
#include <Windows.h>

#include <tchar.h>
#include <process.h>
#include <Strsafe.h>

unsigned int WINAPI ThreadFunc2(LPVOID lp);

unsigned int WINAPI ThreadFunc1(LPVOID lp)
{
	HANDLE hThread = GetCurrentThread();
	HANDLE hTagrealThread = NULL;

	BOOL bOk = DuplicateHandle(GetCurrentProcess(),hThread,GetCurrentProcess(),
		&hTagrealThread,0,FALSE,DUPLICATE_SAME_ACCESS);

	(HANDLE) _beginthreadex(NULL, 0, ThreadFunc2, hTagrealThread, 0, NULL);

	while(1)
	{
		printf("111\n");
		Sleep(1000);
	}
	return 0;
}

unsigned int WINAPI ThreadFunc2(LPVOID lp)
{
	HANDLE hThread = (HANDLE)lp;

	Sleep(5000);

	TerminateThread(hThread,0);

	//必須要關閉句柄
	CloseHandle(hThread);

	return 0;
}

int main()
{
	HANDLE hThread1 =(HANDLE) _beginthreadex(NULL, 0, ThreadFunc1, NULL, 0, NULL );

	WaitForSingleObject(hThread1,INFINITE);

	CloseHandle(hThread1);

	printf("main exit\n");

	system("pause");

	return 0;
}

線程2函數,殺死的是線程1
 最後要關閉線程1的線程內核對象
複製一個句柄後,線程內核對象引用計數會加1

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