DWORD WINAPI WaitForMultipleObjects( __in DWORD nCount, __in const HANDLE* lpHandles, __in BOOL bWaitAll, __in DWORD dwMilliseconds );
更多关于该函数的详细信息,请参看MSDN地址:https://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v%3Dvs.85).aspx。众所周知,函数WaitForMultipleObjects最多只能等待64个句柄,那么如果句柄数量多于64个的时候,我们该如何处理呢?首先总结一下查阅资料时,网友给出的一些说法吧。
1.修改程序设计
对于该方法,现在不作过多讨论。既然这里提出了要突破WaitForMultipleObjects只能等到64个HANDLE的限制,自然有我们提出的原因和意义。
2.大众简易版
哈哈哈,这里之所以叫大众简易版,是因为这个方法在网上随便搜索就能搜索出来,而且实现也简单。直接上代码:
void WaitForMultipleObjectsExpand(HANDLE * handles, DWORD count) { DWORD index = 0; DWORD result = 0; DWORD handleCount = count; /// 每64个HANDLE分为一组 while (handleCount >= MAXIMUM_WAIT_OBJECTS) { WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, &handles[index], TRUE, INFINITE); handleCount -= MAXIMUM_WAIT_OBJECTS; index += MAXIMUM_WAIT_OBJECTS; } if (handleCount > 0) { WaitForMultipleObjects(handleCount, &handles[index], TRUE, INFINITE); } }这种方法是可以等待无限个数的句柄,不过弊端也很明显,撇开效率的问题不说,单是在使用上就存在以下限制:
/****************************************************************************** Module: WaitForMultExp.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <malloc.h> #include <process.h> #include "WaitForMultExp.h" /////////////////////////////////////////////////////////////////////////////// // Internal data structure representing a single expression. // Used to tell OR-threads what objects to wait on. typedef struct { PHANDLE m_phExpObjects; // Points to set of handles DWORD m_nExpObjects; // Number of handles } EXPRESSION, *PEXPRESSION; /////////////////////////////////////////////////////////////////////////////// // The OR-thread function DWORD WINAPI WFME_ThreadExpression(PVOID pvParam) { // This thread function just waits for an expression to come true. // The thread waits in an alertable state so that it can be forced // to stop waiting by queuing an entry to its APC queue. PEXPRESSION pExpression = (PEXPRESSION) pvParam; return(WaitForMultipleObjectsEx( pExpression->m_nExpObjects, pExpression->m_phExpObjects, FALSE, INFINITE, TRUE)); } /////////////////////////////////////////////////////////////////////////////// // This is the APC callback routine function VOID WINAPI WFME_ExpressionAPC(ULONG_PTR dwData) { // This function intentionally left blank } /////////////////////////////////////////////////////////////////////////////// // Function to wait on mutiple Boolean expressions DWORD WINAPI WaitForMultipleExpressions(DWORD nExpObjects, CONST HANDLE* phExpObjects, DWORD dwMilliseconds) { // Allocate a temporary array because we modify the passed array and // we need to add a handle at the end for the hsemOnlyOne semaphore. PHANDLE phExpObjectsTemp = (PHANDLE) _alloca(sizeof(HANDLE) * (nExpObjects + 1)); CopyMemory(phExpObjectsTemp, phExpObjects, sizeof(HANDLE) * nExpObjects); phExpObjectsTemp[nExpObjects] = NULL; // Put sentinel at end // Semaphore to guarantee that only one expression gets satisfied HANDLE hsemOnlyOne = CreateSemaphore(NULL, 1, 1, NULL); // Expression information: 1 per possible thread EXPRESSION Expression[MAXIMUM_WAIT_OBJECTS]; DWORD dwExpNum = 0; // Current expression number DWORD dwNumExps = 0; // Total number of expressions DWORD dwObjBegin = 0; // First index of a set DWORD dwObjCur = 0; // Current index of object in a set DWORD dwThreadId, dwWaitRet = 0; // Array of thread handles for threads: 1 per expression HANDLE ahThreads[MAXIMUM_WAIT_OBJECTS]; // Parse the callers handle list by initializing a structure for // each expression and adding hsemOnlyOne to each expression. while ((dwWaitRet != WAIT_FAILED) && (dwObjCur <= nExpObjects)) { // While no errors, and object handles are in the caller's list... // Find next expression (OR-expressions are separated by NULL handles) while (phExpObjectsTemp[dwObjCur] != NULL) dwObjCur++; // Initialize Expression structure which an OR-thread waits on phExpObjectsTemp[dwObjCur] = hsemOnlyOne; Expression[dwNumExps].m_phExpObjects = &phExpObjectsTemp[dwObjBegin]; Expression[dwNumExps].m_nExpObjects = dwObjCur - dwObjBegin + 1; if (Expression[dwNumExps].m_nExpObjects > MAXIMUM_WAIT_OBJECTS) { // Error: Too many handles in single expression dwWaitRet = WAIT_FAILED; SetLastError(ERROR_SECRET_TOO_LONG); } // Advance to the next expression dwObjBegin = ++dwObjCur; if (++dwNumExps == MAXIMUM_WAIT_OBJECTS) { // Error: Too many expressions dwWaitRet = WAIT_FAILED; SetLastError(ERROR_TOO_MANY_SECRETS); } } if (dwWaitRet != WAIT_FAILED) { // No errors occurred while parsing the handle list // Spawn thread to wait on each expression for (dwExpNum = 0; dwExpNum < dwNumExps; dwExpNum++) { ahThreads[dwExpNum] = chBEGINTHREADEX(NULL, 1, // We only require a small stack WFME_ThreadExpression, &Expression[dwExpNum], 0, &dwThreadId); } // Wait for an expression to come TRUE or for a timeout dwWaitRet = WaitForMultipleObjects(dwExpNum, ahThreads, FALSE, dwMilliseconds); if (WAIT_TIMEOUT == dwWaitRet) { // We timed-out, check if any expressions were satisfied by // checking the state of the hsemOnlyOne semaphore. dwWaitRet = WaitForSingleObject(hsemOnlyOne, 0); if (WAIT_TIMEOUT == dwWaitRet) { // If the semaphore was not signaled, some thread expressions // was satisfied; we need to determine which expression. dwWaitRet = WaitForMultipleObjects(dwExpNum, ahThreads, FALSE, INFINITE); } else { // No expression was satisfied and WaitForSingleObject just gave // us the semaphore so we know that no expression can ever be // satisfied now -- waiting for an expression has timed-out. dwWaitRet = WAIT_TIMEOUT; } } // Break all the waiting expression threads out of their // wait state so that they can terminate cleanly. for (dwExpNum = 0; dwExpNum < dwNumExps; dwExpNum++) { if ((WAIT_TIMEOUT == dwWaitRet) || (dwExpNum != (dwWaitRet - WAIT_OBJECT_0))) { QueueUserAPC(WFME_ExpressionAPC, ahThreads[dwExpNum], 0); } } #ifdef _DEBUG // In debug builds, wait for all of expression threads to terminate // to make sure that we are forcing the threads to wake up. // In non-debug builds, we'll assume that this works and // not keep this thread waiting any longer. WaitForMultipleObjects(dwExpNum, ahThreads, TRUE, INFINITE); #endif // Close our handles to all the expression threads for (dwExpNum = 0; dwExpNum < dwNumExps; dwExpNum++) { CloseHandle(ahThreads[dwExpNum]); } } // error occurred while parsing CloseHandle(hsemOnlyOne); return(dwWaitRet); } //////////////////////////////// End of File //////////////////////////////////通过源代码,我们可以发现WaitForMultipleExpressions函数和WaitForMultipleObjects函数在功能上存在以下几点不同:
4.来自MSDN介绍
To wait on more than MAXIMUM_WAIT_OBJECTS handles, use one of the following methods:
关于第二种用线程池的方法,暂时没研究明白,还望哪个大神能够指点指点。这里只简单说一下第一种方法,即每64个句柄为一组,然后分别创建一个线程调用WaitForMultipleObjects等待一组句柄。其实,这种方法和Windows核心编程的WaitForMultipleExpressions函数的实现方式是异曲同工,所以我们可以参考借鉴WaitForMultipleExpressions函数的实现,实现具有以下功能的函数:
(1)在规定时间内,等待全部句柄处于有信号状态才返回。最多可以等待64*64=4096个句柄。实现如下:
#include<windows.h> #include <malloc.h> #include <process.h> #include "WaitForMultExp.h" #define MAX_KERNEL_OBJS 4096 /////////////////////////////////////////////////////////////////////////////// typedef struct { DWORD nExpObjects; HANDLE *phExpObjects; }EXPRESSION, *LPEXPRESSION; unsigned __stdcall WFME_MultipleObjectsThread(void *lpParam) { //wait for all of kernel objects in a expression to be signaled or a APC to be added to APC Queue in a thread LPEXPRESSION lpExpression = (LPEXPRESSION)lpParam; WaitForMultipleObjectsEx(lpExpression->nExpObjects, lpExpression->phExpObjects, true, INFINITE, true); return 0; } void CALLBACK WFME_MultipleObjectsAPC(ULONG_PTR dwParam) { //only add it to a thread`s APC Queue to stop thread`s waiting } DWORD WINAPI WaitForMultipleObjectsExpand(DWORD nExpObjects, const HANDLE* phExpObjects, DWORD dwMilliSeconds) { EXPRESSION Expressions[MAXIMUM_WAIT_OBJECTS]; HANDLE *phExpObjectsTmp, ahThreads[MAXIMUM_WAIT_OBJECTS]; //allocate the space on stack phExpObjectsTmp = (HANDLE*)_alloca(nExpObjects*sizeof(HANDLE)); CopyMemory(phExpObjectsTmp, phExpObjects, nExpObjects*sizeof(HANDLE)); DWORD dwWaitRet = 0; int nExpObjectsCount = nExpObjects; int NumExps = 0; int BeginPos = 0; int CurPos = 0; if (nExpObjects > MAX_KERNEL_OBJS) { dwWaitRet = WAIT_FAILED; SetLastError(ERROR_SECRET_TOO_LONG); } if (dwWaitRet != WAIT_FAILED) { // break the handles into groups of MAXIMUM_WAIT_OBJECTS while (nExpObjectsCount > MAXIMUM_WAIT_OBJECTS) { Expressions[NumExps].phExpObjects = &phExpObjectsTmp[BeginPos]; Expressions[NumExps].nExpObjects = MAXIMUM_WAIT_OBJECTS; NumExps++; BeginPos += MAXIMUM_WAIT_OBJECTS; nExpObjectsCount -= MAXIMUM_WAIT_OBJECTS; } if (nExpObjectsCount > 0) { Expressions[NumExps].phExpObjects = &phExpObjectsTmp[BeginPos]; Expressions[NumExps].nExpObjects = nExpObjectsCount; NumExps++; } // Create a thread to wait on MAXIMUM_WAIT_OBJECTS handles for (int i = 0; i < NumExps; i++) ahThreads[i] = (HANDLE)_beginthreadex(NULL, 0, WFME_MultipleObjectsThread, (void*)&Expressions[i], 0, NULL); dwWaitRet = WaitForMultipleObjects(NumExps, ahThreads, true, dwMilliSeconds); for (int i = 0; i < NumExps; i++) { if (dwWaitRet == WAIT_TIMEOUT) { QueueUserAPC(WFME_MultipleObjectsAPC, ahThreads[i], 0); } CloseHandle(ahThreads[i]); } } return dwWaitRet; }
#include<windows.h> #include <malloc.h> #include <process.h> #include "WaitForMultExp.h" #define MAX_KERNEL_OBJS 4096 /////////////////////////////////////////////////////////////////////////////// typedef struct { DWORD nExpObjects; HANDLE *phExpObjects; DWORD dwWaitRet; }EXPRESSION, *LPEXPRESSION; unsigned __stdcall WFME_MultipleObjectsThread(void *lpParam) { //wait for all of kernel objects in a expression to be signaled or a APC to be added to APC Queue in a thread LPEXPRESSION lpExpression = (LPEXPRESSION)lpParam; DWORD dwWaitRet = WaitForMultipleObjectsEx(lpExpression->nExpObjects, lpExpression->phExpObjects, false, INFINITE, true); if (dwWaitRet != ERROR_EXE_MARKED_INVALID) { lpExpression->dwWaitRet = dwWaitRet; } return 0; } void CALLBACK WFME_MultipleObjectsAPC(ULONG_PTR dwParam) { //only add it to a thread`s APC Queue to stop thread`s waiting } DWORD WINAPI WaitForMultipleObjectsExpand(DWORD nExpObjects, const HANDLE* phExpObjects, DWORD dwMilliSeconds) { EXPRESSION Expressions[MAXIMUM_WAIT_OBJECTS]; HANDLE *phExpObjectsTmp, ahThreads[MAXIMUM_WAIT_OBJECTS]; //allocate the space on stack phExpObjectsTmp = (HANDLE*)_alloca(nExpObjects*sizeof(HANDLE)); CopyMemory(phExpObjectsTmp, phExpObjects, nExpObjects*sizeof(HANDLE)); DWORD dwWaitRet = 0; int nExpObjectsCount = nExpObjects; int NumExps = 0; int BeginPos = 0; int CurPos = 0; if (nExpObjects > MAX_KERNEL_OBJS) { dwWaitRet = WAIT_FAILED; SetLastError(ERROR_SECRET_TOO_LONG); } if (dwWaitRet != WAIT_FAILED) { // break the handles into groups of MAXIMUM_WAIT_OBJECTS while (nExpObjectsCount > MAXIMUM_WAIT_OBJECTS) { Expressions[NumExps].phExpObjects = &phExpObjectsTmp[BeginPos]; Expressions[NumExps].nExpObjects = MAXIMUM_WAIT_OBJECTS; NumExps++; BeginPos += MAXIMUM_WAIT_OBJECTS; nExpObjectsCount -= MAXIMUM_WAIT_OBJECTS; } if (nExpObjectsCount > 0) { Expressions[NumExps].phExpObjects = &phExpObjectsTmp[BeginPos]; Expressions[NumExps].nExpObjects = nExpObjectsCount; NumExps++; } // Create a thread to wait on MAXIMUM_WAIT_OBJECTS handles for (int i = 0; i < NumExps; i++) ahThreads[i] = (HANDLE)_beginthreadex(NULL, 0, WFME_MultipleObjectsThread, (void*)&Expressions[i], 0, NULL); dwWaitRet = WaitForMultipleObjects(NumExps, ahThreads, false, dwMilliSeconds); for (int i = 0; i < NumExps; i++) { if (dwWaitRet == WAIT_TIMEOUT || (dwWaitRet - WAIT_OBJECT_0) != i) { QueueUserAPC(WFME_MultipleObjectsAPC, ahThreads[i], 0); } CloseHandle(ahThreads[i]); } if (dwWaitRet != WAIT_TIMEOUT) { dwWaitRet = dwWaitRet*MAXIMUM_WAIT_OBJECTS + Expressions[dwWaitRet - WAIT_OBJECT_0].dwWaitRet; } } return dwWaitRet; }上面的这种实现方式其实是有问题的,现将问题说明一下:
这里之所以要把有问题的代码贴出来,是因为当句柄数大于64个时,如果想实现等待到某一个句柄处于有信号状态就返回,在Windows平台下目前我还无法找到一个比较完美的实现方式,这也是写作本文的目的,如果有哪个网友实现了此功能或者有什么更好的想法,希望在看到此文时能够不吝赐教。