当系统创建一个线程的时候,会同时创建一个与线程相关的队列。这个队列被叫做异步过程调用(APC)队列。
为了对线程中的APC队列中的项进行处理,线程必须 将自己设置为可提醒状态,只不过意味着我们的线程在执行的时候已经到达了一个点,在这个点上它能够处理被中断的情况,下边的六个函数能将线程设置为可提醒状态:
BOOL SleepEx();
DWORD WaitForSingleObjectEx();
DWORD WaitForMultipleOBjectsEx();
BOOL SingalObjectAndWait();
BOOL GetQueuedCompletionStatusEx(); //这五个函数的最后一个参数是BOOL值,表示是否设置为可提醒状态。
DWORD MsgWaitForMultipleObjectsEx(); //最后一个参数设置为MWMO_ALERTABLE 标志
当我们调用上边的六个函数之一并将线程设置为可提醒状态的时候,系统首先会检查线程的APC队列,如果队列中至少有一项,那么系统就不会让线程进入睡眠状态。系统将会把那一项取出来并让线程执行与之相关的回调函数。当回调函数返回的时候,会再次检查APC队列中是否还有其他项,如果还有就继续处理,如果没有,对于可提醒函数的调用将会返回。这个是在前边APC中有一项的基础上。
但是当在刚调用函数时,线程的APC的队列中一项都没有,这些函数就会将线程挂起。挂起以后,如果调用了某个函数向该线程的APC队列中添加了一项,则线程将会被唤醒,执行队列中那一项相关联的回调函数后并将该项清除出队列,然后等待函数会立即返回。
微软为我们提供了一个函数,可以手动的将一项添加到线程的APC队列中。
DWORD QueueUserAPC(PAPCFUN pfnAPC,HANDLE hThread,ULONG_PTR dwParam);
pfnAPC:指向回调函数指针。函数原型必须是这个样子:VOID WINAPI APCFUN(ULONG_PTR dwParam);
hThread:线程的句柄,如果是另一个进程中的线程,那么pfnAPC函数内存地址也必须在另一个线程所在的地址空间中。
dwParam:传给回调函数的参数。
可以使用这进行非常高效的线程间的通信,但是只能传入一个值。
这个函数也可以强制让线程退出。加入等待函数正在等待一个内核对象,当这个内核对象还没有触发的时候我们可能要强终止线程或者进程,这样进行线程的强制退出是“干净的”线程退出。
eg:
假如现有一个主线程,主线程创建了一个子线程,子线程中调用了WaitForMultipleObjectEx()并将线程设置为可提醒状态,如果用户立马要终止应用程序:
VOID WINAPI APCFunc(ULONG_PTR dwParam)
{//Nothing To Do};
int WINAPI ThreadFunc(PVOID pvParam)
{
DWORD dw=WaitForSingleObjectEx(hEvent,INFINITE,TRUE);
if(dw==WAIT_OBJECT_0) {} //等待事件成功
if(dw==WAIT_TO_COMPLETION) { return (0);} //APC队列中有项,执行完APCFun之后执行到这个地方 return(0) 线程就会正常结束
..........
return(0);
}
VOID main()
{
HADNLE hEvent=CreateEvent();
HANDLE hThread=CreateThread(NULL,0,ThreadFunc,&hEvent,NULL,NULL);a
QueueUserAPC(APCFunc,hThread,NULL);
WaitForSingleObject(hThread,INFINITE);
CloseHadle(hThread);
CloseHandle(hEvent);
}
当然创建另一个内核事件对象通过调用WaitForMultipleObjects()也可以终止线程,但是WaitForMultipleIbjects()在等待所有的对象都触发的时候,这个是唯一的线程退出的方法。
WXF明 发布了37 篇原创文章 · 获赞 12 · 访问量 9233 私信 关注