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
#include
#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
#include
#include
#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
#include
#include
#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平台下目前我还无法找到一个比较完美的实现方式,这也是写作本文的目的,如果有哪个网友实现了此功能或者有什么更好的想法,希望在看到此文时能够不吝赐教。