1.內(nèi)核對(duì)象
操作系統(tǒng)創(chuàng)建的資源有很多種蘑险,如進(jìn)程、線程岳悟、文件及信號(hào)量佃迄、互斥量等。其中大部分都是通過(guò)程序員的請(qǐng)求創(chuàng)建的贵少,而且請(qǐng)求方式(請(qǐng)求中使用的函數(shù))各不相同呵俏。雖然存在一些差異,但他們之間也有共同點(diǎn):都是由操作系統(tǒng)創(chuàng)建并管理的資源滔灶。
不同資源類型在“管理”方式上也有差異普碎。例如:文件管理中應(yīng)注冊(cè)并更新文件相關(guān)的數(shù)據(jù)I/O位置、文件的打開(kāi)模式等录平。如果是線程麻车,則應(yīng)注冊(cè)并維護(hù)線程ID缀皱、線程所屬進(jìn)程等信息。操作系統(tǒng)為了以記錄相關(guān)信息的方式管理各種資源动猬,在其內(nèi)部生成數(shù)據(jù)塊(可視為結(jié)構(gòu)體變量)啤斗。每種資源需要維護(hù)的信息不同,所以每種資源擁有的數(shù)據(jù)塊格式也有差異赁咙。這類數(shù)據(jù)塊稱為“內(nèi)核對(duì)象”钮莲。
注意:內(nèi)核對(duì)象的所有者是內(nèi)核(操作系統(tǒng)),其含義為:內(nèi)核對(duì)象的創(chuàng)建序目、管理臂痕、銷毀時(shí)機(jī)的決定等工作均由操作系統(tǒng)完成。
2.基于Windows的線程創(chuàng)建
windows創(chuàng)建線程的API原型如下:
#include <windows.h>
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to receive thread ID
);
參數(shù)看起來(lái)有些復(fù)雜猿涨,但只需考慮lpStartAddress和lpParameter這兩個(gè)參數(shù)握童,剩下的傳遞0或NULL即可。
windows線程的銷毀時(shí)間點(diǎn):
windows線程在首次調(diào)用的線程main返回時(shí)銷毀叛赚。還有其它方法可以終止線程澡绩,但最好的辦法就是讓main函數(shù)終止(返回)。
如果線程要調(diào)用C/C++標(biāo)準(zhǔn)函數(shù)俺附,需要通過(guò)如下方法創(chuàng)建線程肥卡。因?yàn)橥ㄟ^(guò)CreateThread函數(shù)調(diào)用創(chuàng)建出的線程在使用C/C++標(biāo)準(zhǔn)函數(shù)時(shí)并不穩(wěn)定。
#include <process.h>
uintptr_t _beginthreadex(
void * security, //線程安全相關(guān)信息事镣,使用默認(rèn)設(shè)置時(shí)傳遞NULL
unsigned stack_size, //要分配給線程的棧大小步鉴,傳遞0時(shí)生成默認(rèn)大小的棧
unsigned (* start_address)(void *), //傳遞線程的main函數(shù)信息
void * arglist, //調(diào)用main時(shí)傳遞的參數(shù)信息
unsigned initflag, //用于指定線程創(chuàng)建后的行為,傳遞0時(shí)璃哟,線程創(chuàng)建后立即進(jìn)入可執(zhí)行狀態(tài)
unsigned * thrdaddr //用于保存線程ID的變量地址值
);
//成功返回線程句柄氛琢,失敗返回0
這個(gè)函數(shù)和CreateThread相比,參數(shù)個(gè)數(shù)以及參數(shù)的含義和順序均相同随闪,只是變量名和參數(shù)類型有所不同阳似。用上面的函數(shù)替換CreateThread時(shí),只需適當(dāng)更改數(shù)據(jù)類型铐伴。
_beginthread函數(shù):
這個(gè)函數(shù)比_beginthreadex更好用撮奏,但該函數(shù)的問(wèn)題在于,它會(huì)讓創(chuàng)建線程時(shí)返回的句柄失效当宴,以防止訪問(wèn)內(nèi)核對(duì)象畜吊。_beginthreadex就是為了解決這一問(wèn)題而定義的函數(shù)。
#include <windows.h>
#include <iostream>
#include <process.h>
unsigned WINAPI ThreadFunc(void *arg);
int main(int argc, char *argv[])
{
HANDLE hThread; //線程句柄
unsigned threadID; //線程ID
int param = 5;
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)param, 0, &threadID);
if(0 == hThread)
{
std::cout << "_beginthreadex() error" << std::endl;
return -1;
}
Sleep(3000);
std::cout << "end of main" << std::endl;
return 0;
}
unsigned WINAPI ThreadFunc(void *arg)
{
int cnt = *((int*)arg);
for(int i = 0; i < cnt; i++)
{
Sleep(1000);
std::cout << "running thread" << std::endl;
}
return 0;
}
句柄户矢、內(nèi)核對(duì)象和ID間的關(guān)系:
線程也屬于操作系統(tǒng)管理的資源玲献,因此會(huì)伴隨內(nèi)核對(duì)象的創(chuàng)建,并為了引用內(nèi)核對(duì)象而返回句柄。
可以通過(guò)句柄區(qū)分內(nèi)核對(duì)象青自,通過(guò)內(nèi)核對(duì)象區(qū)分線程。最終驱证,線程句柄成為區(qū)分線程的工具延窜。通過(guò)_beginthreadex函數(shù)的最后一個(gè)參數(shù)可以獲取線程ID。句柄和ID有如下顯著特點(diǎn):
句柄的整數(shù)值在不同進(jìn)程中可能出現(xiàn)重復(fù)抹锄,但線程ID在跨進(jìn)范圍內(nèi)不會(huì)重復(fù)逆瑞。
線程ID用于區(qū)分操作系統(tǒng)創(chuàng)建的所有線程,但通常沒(méi)有這種需求伙单。
內(nèi)核對(duì)象的2種狀態(tài):
資源類型不同获高,內(nèi)核對(duì)象也含有不同信息。其中吻育,應(yīng)用程序?qū)崿F(xiàn)過(guò)程中需要特別關(guān)注的信息被賦予某種“狀態(tài)”念秧。例如:線程內(nèi)核對(duì)象中需要重點(diǎn)關(guān)注線程是否已終止,所以終止?fàn)顟B(tài)又稱為“signaled狀態(tài)”布疼,未終止?fàn)顟B(tài)被稱為“non-signaled狀態(tài)”摊趾。
內(nèi)核對(duì)象的狀態(tài)及狀態(tài)查看:
進(jìn)程和線程的內(nèi)核對(duì)象初始狀態(tài)是non-signaled狀態(tài)。內(nèi)核對(duì)象帶有一個(gè)boolean變量游两,其初始值為FALSE砾层,此時(shí)的狀態(tài)就是non-signaled狀態(tài)。如果發(fā)生了事件贱案,把該變量變?yōu)門RUE肛炮,此時(shí)的狀態(tài)就是signaled狀態(tài)。內(nèi)核對(duì)象類型不同宝踪,進(jìn)入signaled的狀態(tài)也有所區(qū)別侨糟。
系統(tǒng)還定義了WaitForSingleObject和WaitForMultipleObjects兩個(gè)函數(shù)。
首先介紹WaitForSingleObject函數(shù),該函數(shù)針對(duì)單個(gè)內(nèi)核對(duì)象驗(yàn)證signaled狀態(tài)逛球。
DWORD WaitForSingleObject(
HANDLE hHandle, //handle to object to wait for
DWORD dwMilliseconds //time-out interval in milliseconds
);
//返回值:進(jìn)入signaled狀態(tài)返回WAIT_OBJECT_0茫陆,超時(shí)返回WAIT_TIMEOUT
該函數(shù)由于發(fā)生事件(變?yōu)閟ignaled狀態(tài))返回時(shí),有時(shí)會(huì)把相應(yīng)內(nèi)核對(duì)象再次更改為non-signaled狀態(tài)悲幅。這種可以再次進(jìn)入non-signaled狀態(tài)的內(nèi)核對(duì)象稱為“auto-reset模式”的內(nèi)核對(duì)象,而不會(huì)自動(dòng)跳轉(zhuǎn)到non-signaled狀態(tài)的內(nèi)核對(duì)象稱為“manual-reset模式”站蝠。下面的函數(shù)與上述函數(shù)不同汰具,可以驗(yàn)證多個(gè)內(nèi)核對(duì)象狀態(tài)。
DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in the handle array
CONST HANDLE *lpHandles, // pointer to the object-handle array
BOOL fWaitAll, // wait flag
DWORD dwMilliseconds // time-out interval in milliseconds
);
修改后的程序:
#include <windows.h>
#include <iostream>
#include <process.h>
unsigned WINAPI ThreadFunc(void *arg);
int main(int argc, char *argv[])
{
HANDLE hThread; //線程句柄
DWORD wr;
unsigned threadID; //線程ID
int param = 5;
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)param, 0, &threadID);
if (0 == hThread)
{
std::cout << "_beginthreadex() error" << std::endl;
return -1;
}
//傳遞INFINITE時(shí)函數(shù)不會(huì)返回菱魔,直到內(nèi)核對(duì)象變成signaled狀態(tài)
if ((wr = WaitForSingleObject(hThread, INFINITE)) == WAIT_FAILED)
{
std::cout << "thread wait error" << std::endl;
return -1;
}
std::cout << "wait result:" << ((wr == WAIT_OBJECT_0) ? "signed" : "time-out");
std::cout << std::endl;
std::cout << "end of main" << std::endl;
return 0;
}
unsigned WINAPI ThreadFunc(void *arg)
{
int cnt = *((int*)arg);
for (int i = 0; i < cnt; i++)
{
Sleep(1000);
std::cout << cnt << "running thread" << std::endl;
}
return 0;
}