windows核心编程之使用线程APC回调安全退出多个等待线程

前言

程序开发中经常遇到需要这些情况:辅助线程正在等待内核对象的触发,主线程需要强制终止辅助线程。我们常常做的就是使用:TerminateThread来强制终止线程。这样做当然是不太好的,强制终止线程后系统不会销毁此线程的堆栈,长久下去内存泄露问题就会很严重了。线程最安全的退出方式当然还是让它自己返回了。本文主要介绍windows核心编程中介绍的一种安全退出线程方式:使用可等待API等待内核对象触发,添加线程APC回调。

API介绍

首先得简单介绍下一个重要的windows API

DWORD WINAPI QueueUserAPC( __inPAPCFUNC pfnAPC, __inHANDLE hThread, __inULONG_PTR dwData);

函数允许我们手动将一个APC回调添加到指定线程队列中,回调函数:VOID WINAPI ApcCallback1(ULONG_PTR dwParam)这个线程可以是系统中任何线程,如果标识的线程在另一个进程中,那么回调函数的地址也必须在另一个线程的地址空间中。QueueUserAPC也可以用来强制让线程退出等待状态,当线程正在等待内核对象触发,可以使用它强制唤醒正在等待的线程,并让它将自己杀死。

另外就是Windows提供了6个API,可将线程的状态设置为可提醒状态。

SleepEx(__in DWORD dwMilliseconds,__in BOOL bAlertable);WaitForSingleObjectEx(__in HANDLE hHandle,__in DWORD dwMilliseconds,__in BOOL bAlertable);WaitForMultipleObjectsEx(__in DWORD nCount,__in_ecount(nCount) CONST HANDLE *lpHandles,__in BOOL bWaitAll,__in DWORD dwMilliseconds,__in BOOL bAlertable);SignalObjectAndWait(__in HANDLE hObjectToSignal,__in HANDLE hObjectToWaitOn,__in DWORD dwMilliseconds,__in BOOL bAlertable);GetQueuedCompletionStatusExMsgWaitForMultipleObjectsEx 使用MWMO_ALERTABLE标识线程进入可等待状态这五个函数的老版本API,即不带Ex的在内部实现都是调用对应的Ex函数,传入bAlertable=FALSE。

在可提醒I/O中将对这些函数的调用以及完成回调、APC回调进行详细的介绍,这里暂时省略了。

实例代码//给每一个线程标记上一个索引值volatile LONGg_nIndex = 0;UINT __stdcall Thread1(void* lpParam){//使用原子锁来操作实现用户模式下的线程同步,更加快捷LONG lPrevIndex = InterlockedExchangeAdd(&g_nIndex, 1);//我们需要记录原始值LONG lCurIndex= lPrevIndex + 1;cout<<"线程"<<lCurIndex<<"开始执行"<<endl;while( true ){//cout<<"Thread1:"<<nIndex<<endl;//休眠一秒钟,线程进入可提醒状态if ( WAIT_IO_COMPLETION == SleepEx(1000, TRUE) ){//此时,我们添加一项到APC队列中了,线程可以退出了break;}//nIndex++;}cout<<"线程"<<lCurIndex<<"正常退出"<<endl;return 0;}//APC回调函数VOID WINAPI ApcCallback(ULONG_PTR dwParam){//由于只是为了让线程立即正常退出,而不必杀死线程,我们在这里可以不做任何处理cout<<"ApcCallback:线程的APC回调函数执行"<<endl;}bool ApcTest(){const int nThreadCount = 10;HANDLE hThread[nThreadCount];int i = 0; for ( int i=0; i<nThreadCount; ++i )hThread[i] = (HANDLE)_beginthreadex(NULL, 0, Thread1, NULL, 0, NULL);DWORD dwError, dwRet;while( true ){dwRet = WaitForMultipleObjects(nThreadCount, hThread, TRUE, 10*1000);if ( dwRet == WAIT_TIMEOUT ){//等待超时,想办法强制终止线程//由于辅助线程休眠时进入可等待状态,,手动添加回调到APC队列将导致毁掉执行完后线程清空队列,线程退出可提醒状态//此时,友好地退出线程。for ( i=0; i<nThreadCount; ++i ){dwRet = QueueUserAPC(ApcCallback, hThread[i], 2015);if ( dwRet == 0 ){//添加APC回调到线程APC队列失败dwError = GetLastError();break;}}continue;}if ( WAIT_OBJECT_0 == dwRet ){cout<<"辅助线程均已退出,循环退出…………"<<endl;break;}}for ( i=0; i<nThreadCount; ++i )CloseHandle(hThread[i]);return true;}程序运行

总结

主要就是在线程等待时,使用那些扩展的API(带Ex),使线程进入可等待状态。这时,系统会先检查线程的APC队列。由于我们向线程的APC队列中人为添加了一个回调函数,队列不为空则调用APC函数,清空队列。然后函数执行完毕返回WAIT_IO_COMPLETION,这时我们就知道是自己添加了一个APC回调,引导线程自己退出。从而实现多个等待线程安全地退出等待状态。

如果心在远方,只需勇敢前行,梦想自会引路,

windows核心编程之使用线程APC回调安全退出多个等待线程

相关文章:

你感兴趣的文章:

标签云: