《Windows核心编程》中的 WaitForMultExp ( 积分: 300 )

  • 主题发起人 主题发起人 刘麻子
  • 开始时间 开始时间

刘麻子

Unregistered / Unconfirmed
GUEST, unregistred user!
第十章: 线程同步工具包 , 第4节: 实现一个WaitForMultipleExpressions()函数,
此函数可以等待最多64组内核对象, 每组可以包含最多63个内核对象, 只要有任一组对象
等待成功, 则WaitForMultipleExpressions()函数成功, 并返回: 组号 + WAIT_OBJECT_0
 
第十章: 线程同步工具包 , 第4节: 实现一个WaitForMultipleExpressions()函数,
此函数可以等待最多64组内核对象, 每组可以包含最多63个内核对象, 只要有任一组对象
等待成功, 则WaitForMultipleExpressions()函数成功, 并返回: 组号 + WAIT_OBJECT_0
 
原书光盘C代码对WaitForMultipleExpressions()的描述:
1. WaitForMultExp.h 文件
/******************************************************************************
Module: WaitForMultExp.h
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/

#pragma once

///////////////////////////////////////////////////////////////////////////////

DWORD WINAPI WaitForMultipleExpressions(DWORD nExpObjects,
CONST HANDLE* phExpObjects, DWORD dwMilliseconds);

//////////////////////////////// End of File //////////////////////////////////

2.WaitForMultExp.cpp 文件
/******************************************************************************
Module: WaitForMultExp.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/

#include "../CmnHdr.h"
/* See Appendix A. */
#include <malloc.h>
#include <process.h>
#include &quot;WaitForMultExp.h&quot;

///////////////////////////////////////////////////////////////////////////////

// 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,
TRUE, 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) &amp;&amp;
(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 = &amp;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] = chbegin
THREADEX(NULL,
1, // We only require a small stack
WFME_ThreadExpression, &amp;Expression[dwExpNum],
0, &amp;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 //////////////////////////////////
 
我由C转换来的PASCAL代码: (WaitForMultExp.pas 文件)
unit WaitForMultExp;
interface
uses Windows;
function WaitForMultipleExpressions(nExpObjects: DWORD;
phExpObjects: PWOHandleArray;
dwMilliseconds: DWORD): DWORD;
stdcall;
implementation
type
// 单个表达式结构
PExpression = ^TExpression;
TExpression = record
m_phExpObjects: PWOHandleArray;
// 列表位置
m_nExpObjects: DWORD;
// 列表长度
end;

// 单个表达式线程
function WFME_ThreadExpression(var Expression: TExpression): Integer;
begin
Result := WaitForMultipleObjectsEx(
Expression.m_nExpObjects, Expression.m_phExpObjects, TRUE, INFINITE, TRUE);
end;

// 空的APC回调函数
procedure WFME_ExpressionAPC(dwData: DWORD);
stdcall;
begin
// 没有实际动作, 仅用作促使WaitForMultipleObjectsEx()返回
end;

// 扩展的WaitForXX
function WaitForMultipleExpressions(nExpObjects: DWORD;
phExpObjects: PWOHandleArray;
dwMilliseconds: DWORD): DWORD;
stdcall;
var
phExpObjectsTemp: PWOHandleArray;
hsemOnlyOne: THandle;
// 单计数信号量内核对象
Expression: array[0..MAXIMUM_WAIT_OBJECTS - 1] of TExpression;
// 表达式数组
ahThreads: array[0..MAXIMUM_WAIT_OBJECTS - 1] of THandle;
// 线程句柄数组
dwExpNum, dwNumExps, dwObjbegin
, dwObjCur, dwThreadId: DWORD;
begin
// 申请内存并拷贝句柄列表
GetMem(phExpObjectsTemp, SizeOf(THandle) * (nExpObjects + 1));
CopyMemory(phExpObjectsTemp, phExpObjects, SizeOf(THandle) * nExpObjects);
phExpObjectsTemp[nExpObjects] := 0;
// 防止多个线程等待成功, (而造成多次改变对象状态)
hsemOnlyOne := CreateSemaphore(nil, 1, 1, nil);
// 分析调用者所给句柄列表
dwNumExps := 0;
dwObjbegin
:= 0;
dwObjCur := 0;
Result := 0;
while (Result <> WAIT_FAILED) and (dwObjCur <= nExpObjects)do
begin
// 定位单个表达式尾部
while (phExpObjectsTemp[dwObjCur] <> 0)do
Inc(dwObjCur);
// 尾部加入信号量句柄
phExpObjectsTemp[dwObjCur] := hsemOnlyOne;
// 填写单个表达式结构
Expression[dwNumExps].m_phExpObjects := @phExpObjectsTemp[dwObjbegin
];
Expression[dwNumExps].m_nExpObjects := dwObjCur - dwObjbegin
+ 1;
// 表达式内部句柄过多
if (Expression[dwNumExps].m_nExpObjects > MAXIMUM_WAIT_OBJECTS) then
begin
Result := WAIT_FAILED;
SetLastError(ERROR_SECRET_TOO_LONG);
end;

// 准备处理下个表达式
Inc(dwObjCur);
dwObjbegin
:= dwObjCur;
Inc(dwNumExps);
// 原意: 表达式数量过多
// 疑问: 当处理到第64个表达式, dwNumExps应该就等于64, 但目前表达式个数并未超过64
if (dwNumExps = MAXIMUM_WAIT_OBJECTS) then
begin
Result := WAIT_FAILED;
SetLastError(ERROR_TOO_MANY_SECRETS);
end;
end;

// 所给句柄列表没有问题
if (Result <> WAIT_FAILED) then
begin
// 为每个表达式建立线程
for dwExpNum := 0 to dwNumExps - 1do
begin
ahThreads[dwExpNum] :=
begin
Thread(nil, 0, @WFME_ThreadExpression, @Expression[dwExpNum], 0, dwThreadId);
end;

// 等待某一个表达式成立
Result := WaitForMultipleObjects(dwNumExps, @ahThreads[0], FALSE, dwMilliseconds);
// 超时, 准备按超时处理
if (Result = WAIT_TIMEOUT) then
begin
// 阻止表达式线程等待成功
Result := WaitForSingleObject(hsemOnlyOne, 0);
// 如果等待信号量hsemOnlyOne超时,
// 说明有表达式线程刚好等待成功
if (WAIT_TIMEOUT = Result) then
begin
// 找出等待成功的线程
Result := WaitForMultipleObjects(dwNumExps, @ahThreads[0], FALSE, INFINITE);
end else
begin
// 否则, 按照超时处理
Result := WAIT_TIMEOUT;
end;
end;

// 唤醒仍在等待的表达式线程
for dwExpNum := 0 to dwNumExps - 1do
begin
if (WAIT_TIMEOUT = Result) or (dwExpNum <> (Result - WAIT_OBJECT_0)) then
QueueUserAPC(@WFME_ExpressionAPC, ahThreads[dwExpNum], 0);
end;

{$IFDEF DEBUG}
WaitForMultipleObjects(dwExpNum, @ahThreads[0], TRUE, INFINITE);
{$ENDIF}
// 关闭线程(内核对象)句柄
for dwExpNum := 0 to dwNumExps - 1do
CloseHandle(ahThreads[dwExpNum]);
end;

// 清除信号量(内核)对象
CloseHandle(hsemOnlyOne);
end;

end.
 
完整代码下载(此链接40分钟后失效):
http://upserver3.ys168.com/ys168up/D1/Ys.aspx?f=10-WaitForMultExp.rary68z73f8b3f8b3f9b4z95b0f8b2b3f9b2f5f8f8b0f9f9b3f6e14z97e14e24b1f9f2f9b4f8f6b2b7z
或至 http://liumazi.ys168.com 下载TempFile目录下的 10-WaitForMultExp.rar 文件
 
捡了。。。。 [:D]
 
各位, 我想讨论的问题是, WaitForMultipleExpressions() 函数中的
while (Result <> WAIT_FAILED) and (dwObjCur <= nExpObjects)do
循环中
最后的这个片断及其所处位置是否合适:
-------------------------------------------------
................
if (dwNumExps = MAXIMUM_WAIT_OBJECTS) then
begin
Result := WAIT_FAILED;
SetLastError(ERROR_TOO_MANY_SECRETS);
end;
--------------------------------------------------
我觉得, 按照代码, 处理完第64组'表达式', dwNumExps应该就等于64了,
但并不能就此认为表达式过多, 因为, 有可能之后就跳出While循环了 ....
我觉得把上述片断放到While循环的开头会更好, 各位, 你们的看法呢?
 
不合适,我是欧阳锋1号..........
 
这应该是保险的做法,有时候我编程序也时常有这种做法.
 
谢谢诸位参与讨论,
To aleyn:
保险的做法? 我的意思,不知道表达清楚了没有.
我是觉得那个if判断不应该放在while循环的末尾,
否则处理完第64个表达式,就会认为表达式过多了 ..
正确的, 把if判断放在while循环的开头才对吧 ?
 
刚认识你时,觉得你我水平差不多臭 ^_^,一年多一点,麻子,你的进步神速!
好好努力!
 
if (++dwNumExps == MAXIMUM_WAIT_OBJECTS) ....
这是C的原句子,你写的是:
if (dwNumExps = MAXIMUM_WAIT_OBJECTS) then
两者意思不同。++dwNumExps是,假设dwNumExps=64,这里前缀++表示用一个内存新变量保存一下64+1的数字(65)再用这个临时变量计算,因此这里是65而不是64,如果改的话,可以放到while前面,否则就手工写
dwNumExpsInc := dwNumExps + 1;
if (dwNumExpsInc = MAXIMUM_WAIT_OBJECTS) then
呵呵,C的语法我好久没碰了,但愿我没理解错:)要是真理解错了,请大家不要笑话:)
 
to scan兄:
经你这么一说, 还真有点怀念我们初识的那段日子了, 有机会聚聚~~~ [:D]

to zqw0117兄:
您对C代码的理解是对的, 不过, 请注意 if (dwNumExps = MAXIMUM_WAIT_OBJECTS) then
上面还有一个 Inc(dwNumExps);
, 可能因为没和 if 连着写在一块, 不容易看到. [:)]
另, dwNumExps 第1次循环时等于 0, 待处理完第1个表达式之后, Inc(dwNumExps) 了,
才等于 1 的, 依此类推, 处理第64个时dwNumExps等于63, Inc(dwNumExps)后就等于64.. 不是吗?

也许我的理解有不当之处, 欢迎诸位继续讨论, 小弟洗耳恭听 ..
 
刘麻子兄 治学严谨,是我等学习的榜样!
你说的不错,这里的确是有一小点问题的,不过也无伤大雅,作者举这个例子主要目的在于通过线程和异步过程调用来实现“分组”等待内核对象或突破等待的内核对象个数的限制,穷究到底是63组抑或是64组,那好象是有点吹毛求疵啦。[:D]
 
lichengbin兄所言甚是, 的确不是什么大问题, 没必要太较真~
其实主要是怕自己理解错误, 所以才发帖子来问问大家的看法~~
 
暂且这样,平分了吧. ~
 
后退
顶部