Multithreading 为什么WaitForMultipleObjects会因多个线程句柄而失败?
在下面的测试程序中,每个测试线程在开始执行时将其句柄添加到全局Multithreading 为什么WaitForMultipleObjects会因多个线程句柄而失败?,multithreading,delphi,delphi-xe2,Multithreading,Delphi,Delphi Xe2,在下面的测试程序中,每个测试线程在开始执行时将其句柄添加到全局TThreadList,并在执行即将结束时从同一列表中删除其句柄 此外,出于测试目的,每个线程确保在主线程锁定列表之前添加其句柄(复制它们的句柄并开始等待它们完成)。线程还确保在主线程锁定列表之前不会移除句柄 测试程序可以正常运行大约50-60个线程。在此之后,WaitForMultipleObjects调用start to fail withWAIT\u FAILED,GetLastError返回87(ERROR\u无效\u参数)。
TThreadList
,并在执行即将结束时从同一列表中删除其句柄
此外,出于测试目的,每个线程确保在主线程锁定列表之前添加其句柄(复制它们的句柄并开始等待它们完成)。线程还确保在主线程锁定列表之前不会移除句柄
测试程序可以正常运行大约50-60个线程。在此之后,WaitForMultipleObjects
调用start to fail withWAIT\u FAILED
,GetLastError
返回87(ERROR\u无效\u参数
)。目前它启动100个线程。我的问题是,我做错了什么
该程序是用XE2-update4,32位目标平台编译的。测试箱为W7x64
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
windows,
sysutils,
classes,
syncobjs;
type
TTestThread = class(TThread)
private
FAckStarted: TEvent;
function GetAckHandle: THandle;
class var
ThreadList: TThreadList;
WaitEnd: THandle;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
property AckHandle: THandle read GetAckHandle;
end;
{.$DEFINE FREEONTERMINATE}
constructor TTestThread.Create;
begin
inherited Create(True);
FAckStarted := TEvent.Create;
{$IFDEF FREEONTERMINATE}
FreeOnTerminate := True;
{$ENDIF}
end;
destructor TTestThread.Destroy;
begin
FAckStarted.Free;
inherited;
end;
procedure TTestThread.Execute;
begin
// OutputDebugString(PChar(Format('%d starting -------------', [Handle])));
ThreadList.Add(Pointer(Handle));
FAckStarted.SetEvent;
NameThreadForDebugging(AnsiString(IntToStr(Handle)));
WaitForSingleObject(WaitEnd, INFINITE);
ThreadList.Remove(Pointer(Handle));
// OutputDebugString(PChar(Format('%d leaving -------------', [Handle])));
end;
function TTestThread.GetAckHandle: THandle;
begin
Result := FAckStarted.Handle;
end;
const
NumThreads = 100;
var
DeferThreadEnd: TEvent;
ThreadList: array of TThread;
i: Integer;
Thread: TTestThread;
WaitThreadStart: THandle;
LockList: TList;
LockListCount: Integer;
ThreadHandleArr: array of THandle;
WaitRet: DWORD;
begin
IsMultiThread := True;
ReportMemoryLeaksOnShutdown := True;
TTestThread.ThreadList := TThreadList.Create;
DeferThreadEnd := TEvent.Create;
TTestThread.WaitEnd := DeferThreadEnd.Handle;
SetLength(ThreadList, NumThreads);
for i := 0 to NumThreads - 1 do begin
Thread := TTestThread.Create;
ThreadList[i] := Thread;
WaitThreadStart := Thread.GetAckHandle;
Thread.Start;
WaitForSingleObject(WaitThreadStart, INFINITE);
end;
LockList := TTestThread.ThreadList.LockList;
LockListCount := LockList.Count;
SetLength(ThreadHandleArr, LockListCount);
for i := 0 to LockListCount - 1 do
{$IFDEF FREEONTERMINATE}
Win32Check(DuplicateHandle(GetCurrentProcess, THandle(LockList[i]),
GetCurrentProcess, @ThreadHandleArr[i], SYNCHRONIZE, True, 0));
{$ELSE}
ThreadHandleArr[i] := THandle(LockList[i]);
{$ENDIF}
TTestThread.ThreadList.UnlockList;
DeferThreadEnd.SetEvent;
if LockListCount > 0 then begin
Writeln('waiting for ', LockListCount, ' threads');
WaitRet := WaitForMultipleObjects(LockListCount,
PWOHandleArray(ThreadHandleArr), True, INFINITE);
case WaitRet of
WAIT_OBJECT_0: Writeln('wait success');
WAIT_FAILED: Writeln('wait fail:', SysErrorMessage(GetLastError));
end;
end;
for i := 0 to Length(ThreadList) - 1 do begin
{$IFDEF FREEONTERMINATE}
Win32Check(CloseHandle(ThreadHandleArr[i]));
{$ELSE}
ThreadList[i].Free;
{$ENDIF}
end;
DeferThreadEnd.Free;
TTestThread.ThreadList.Free;
Writeln('program end');
Readln;
end.
WaitForMultipleObjects()
声明:
对象句柄的最大数目为最大等待对象数
MAXIMUM\u WAIT\u OBJECTS
的值为64(在winnt.h
中定义),因此超过了100个句柄的限制。但是,本文档还说明了如何克服该限制:
要等待多个最大\u wait\u对象句柄,请使用以下方法之一:
- 创建一个线程以等待最大的\u wait\u对象句柄,然后等待该线程和其他句柄。使用此技术可以将控制柄拆分为最大\u等待\u对象组
- 调用RegisterWaitForSingleObject等待每个句柄。线程池中的等待线程等待已注册对象的最大\u wait\u对象,并在对象发出信号或超时间隔过期后分配工作线程
有关第一种技术的示例,请参见的答案。也可以使用以下变量:联锁递增/递减计数器和单个事件等待。感谢编辑。我觉得有点不好,因为有一个比我更好的答案:-)还要注意,64对象限制实际上是内核端限制。重新定义MAXIMUM_WAIT_对象,甚至重新实现WaitForMultipleObjects的用户端,都不会有多大作用。你只需要绕过这个特定的限制。