一:BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode);
该函数可以决定一个线程是否还在执行,只要不断的检测返回值就可以判断线程是否结束。但是不断的检测会浪费CPU事件。常说的busy loop就是这种现象。
二:DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
hHandle:等待对象的句柄(代表一个核心对象),如线程句柄。
dwMilliseconds:时间间隔,即使对象未激活,时间到了还是会返回
1:此函数返回成功的三个因素:
1)等待的目标(核心对象)变成激发状态,返回WAIT_OBJECT_0;
2)核心对象变成激发状态前,等待时间到了,返回WAIT_TIMEOUT;
3)如果一个拥有MUTEX(互斥器)的线程结束前没有释放MUTEX,则传回WAIT_ABANDONED。
当线程正在执行时,线程对象处于未激活状态。当线程结束时,线程对象就会被激活,函数WaitForSingleObject就会醒来。需要注意的是:醒来未必立即被调动,而只是从休眠态队列排入就绪队列,等待操作系统调度。
HANDLE g_hEvent;
UINT _stdcall ChildFunc(LPVOID);
int main(int argc, char* argv[])
{
HANDLE hChildThread;
UINT uId;
// 创建一个自动重置的事件
g_hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
hChildThread = (HANDLE)::_beginthreadex(NULL, 0, ChildFunc, NULL, 0, &uId);
//用户输入字符后,就会激活时间
getchar();
::SetEvent(g_hEvent);
// 等待子线程完成工作,激活线程对象
::WaitForSingleObject(hChildThread, INFINITE);
::CloseHandle(hChildThread);
::CloseHandle(g_hEvent);
return 0;
}
UINT _stdcall ChildFunc(LPVOID)
{
//等待事件被激活,
::WaitForSingleObject(g_hEvent, INFINITE);
printf(" Child thread is working....../n");
::Sleep(5*1000);
return 0;
}
2:那么哪些对象对象是被激发的对象。
对于线程/进程,如果还在运行,该对象就处于未激发;如果运行结束,则转入激发状态。
对于文件对象,如果一个I/O操作完成,该文件对象即处于激发状态;
对于事件,CreateEvent()初态为TRUE,或SetEvent()、PulseEvent(),事件为激发态;
对于互斥器,如果未被任何进程锁定,该对象处于激发状态;
对于信号量,如果信号量的现值大于0,该对象处于激发状态;
对于控制台输入,当Console窗口的输入缓冲区中有数据可用时,此对象被激发。
三:DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll,DWORD dwMilliseconds );
nCount 句柄的数量 最大值为MAXIMUM_WAIT_OBJECTS(64)
HANDLE 句柄数组的指针。不需要为相同类型
HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore )数组
BOOL bWaitAll 等待的类型,如果为TRUE 则等待所有信号量有效在往下执行,FALSE 当有其中一个信号量有效时就向下执行
DWORD dwMilliseconds 超时时间 超时后向执行。 如果为WSA_INFINITE 永不超时。如果没有信号量就会在这死等。
1:返回值
1)等待时间到了,返回WAIT_TIMEOUT;
2)当bWaitAll是True的时候正常返回值是 WAIT_OBJECT_0;
3)当bWaitAll是false的时候,返回值减去WAIT_OBJECT_0,就是表示数组中哪一个Handle被激活了。
4)如果等待对象中有Mutex,则传回值范围从WAIT_ABANDONED_0到WAIT_ABANDONED_0 + nCount – 1。
DWORD WINAPI ThreadFunc(LPVOID);
#define THREAD_POOL_SIZE 3
#define MAX_THREAD_INDEX THREAD_POOL_SIZE-1
#define NUM_TASKS 6
int main()
{
HANDLE hThrds[THREAD_POOL_SIZE];
int slot = 0;
DWORD threadId;
int i;
DWORD rc;
for (i=1; i<=NUM_TASKS; i++)
{
if (i > THREAD_POOL_SIZE)
{
//只要有一个线程被激活了,就返回
rc = WaitForMultipleObjects( THREAD_POOL_SIZE,hThrds,FALSE,INFINITE );
//获取被激活的线程
slot = rc - WAIT_OBJECT_0;
}
//创建线程,赋值给刚刚被激活的线程数组
hThrds[slot] = CreateThread(NULL,0,ThreadFunc,(LPVOID)slot,0,&threadId )
slot++;
}
//等待所有线程都结束后,推出
rc = WaitForMultipleObjects(THREAD_POOL_SIZE,hThrds,TRUE,INFINITE );
for (slot=0; slot<THREAD_POOL_SIZE; slot++)
CloseHandle(hThrds[slot]);
return EXIT_SUCCESS;
}
DWORD WINAPI ThreadFunc(LPVOID n)
{
srand( GetTickCount() );
Sleep((rand()%10)*800+500);
printf("Slot %d idle/n", n);
return ((DWORD)n);
}
四:DWORD MsgWaitForMultipleObjects(DWORD nCount, LPHANDLE pHandles, BOOL fWaitAll, DWORD dwMilliseconds, DWORD dwWakeMask );
如果只是使用WaitForSingleObjects()或WaitForMultipleObjects(),你根本就无法回到主消息循环里。这样,应用程序的用户界面停止重绘,菜单工具条无法响应。
dwWakeMask :添加到对象数组中的句柄,pHandles除了用户在数组总制定的外,还可以添加消息的激活事件对象。
1:返回值,多一个WAIT_OBJECT_0+ nCount,表示“指定的消息到达队列”。
2:当主线程要退出时,为了能保证线程的资源能全部地释放,主线程必须等待工作线程退出。线程对象和进程对象一样,也是内核对象,而且线程对象的特点是当 线程退出时,线程内核对象会自动变为有信号状态,能够唤醒所有正在等待它的线程。我们通常都习惯于使用WaitForSingleObject等函数来等 待某个内核对象变为有信号状态,但是在主线程中不要使用WaitForSingleObject和WaitForMultipleObjects两个函数 等待线程退出,其原因就是有导致程序死锁的隐患,特别是线程函数里调用了SendMessage或是直接操作了MFC对象,更易出现此种现象。
比如,在主线程中调WaitForSingleObject等子线程结束,而此时CPU切换到子线程函数体内执行,而子线程函数体这是可能的某个步骤需要 住线程完成,这是通过SendMesage发送消息给主线程,由于主线程已经挂起,所以没有机会去消息队列中抓取消息并处理它,结果导致函数不会返回,工 作线程也被挂起,从而导致死锁。
int nWaitCount = 2
while (1)
{
dRet=MsgWaitForMultipleObjects(nWaitCount,hArray,FALSE,INFINITE,QS_ALLINPUT);
if (dRet == WAIT_OBJECT_0+ nWaitCount)
{
//当函数由于消息到来而返回,则需要用户主动去消息队列中将消息抓取出来,然后派发出去,这样该消息就会被处理了。
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else if (dRet >= WAIT_OBJECT_0 && dRet < WAIT_OBJECT_0+ nWaitCount)
{
nExitThreadCount++;
//退出了一个线程
if (nExitThreadCount == 1)
{
//获取哪个线程退出,并从对象数组中去除该线程,同时修改对象数。
int nIndex=dRet-WAIT_OBJECT_0;
hArray[nIndex]=hArray[nWaitCount-1];
hArray[nWaitCount-1]=NULL;
nWaitCount--;
}
else
{
//两个线程都退出了
break;
}
}
else
{
DWORD dErrCode=GetLastError();
break;
}
}