线程的APC机制

谁都会走 提交于 2020-01-26 22:30:26

当系统创建一个线程的时候,会同时创建一个与线程相关的队列。这个队列被叫做异步过程调用(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()在等待所有的对象都触发的时候,这个是唯一的线程退出的方法。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!