निम्नलिखित परीक्षण कार्यक्रम में प्रत्येक टेस्ट थ्रेड अपने हैंडल को ग्लोबल TThreadList
पर निष्पादित करता है जब इसे निष्पादित करना शुरू होता है, और उसके निष्पादन को समाप्त होने पर उसी सूची से अपने हैंडल को हटा देता है।WaitForMultipleObjects एकाधिक थ्रेड हैंडल के साथ विफल क्यों होता है?
परीक्षण उद्देश्यों के लिए, इसके अतिरिक्त, प्रत्येक थ्रेड सुनिश्चित करता है कि यह मुख्य थ्रेड सूची को लॉक करने से पहले अपने हैंडल को जोड़ता है (उनके हैंडल को डुप्लिकेट करने और उन्हें समाप्त करने के लिए प्रतीक्षा करना)। थ्रेड भी सुनिश्चित करते हैं कि मुख्य थ्रेड सूची को लॉक करने से पहले वे अपने हैंडल को नहीं हटाते हैं।
परीक्षण कार्यक्रम लगभग 50-60 धागे के लिए ठीक चलाता है। उसके बाद, WaitForMultipleObjects
कॉल WAIT_FAILED
, GetLastError
रिटर्न 87 (ERROR_INVALID_PARAMETER
) के साथ असफल होने लगती है। वर्तमान में यह 100 धागे शुरू करता है। मेरा सवाल है, मैं क्या गलत कर रहा हूँ?
प्रोग्राम XE2 - अद्यतन 4, 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.
[इस दृष्टिकोण] (http://tondrej.blogspot.com/2016/03/the-strange-limitation-of-64-threads.html) की विविधता का उपयोग करने की संभावना भी है: इंटरलॉक-वृद्धि/कम करने के लिए काउंटर और एक भी घटना की प्रतीक्षा की। –
संपादन के लिए धन्यवाद। मुझे अपने से बेहतर जवाब के लिए थोड़ा बुरा क्रेडिट लगता है :-) –
यह भी ध्यान दें कि 64 ऑब्जेक्ट सीमा वास्तव में एक कर्नेल पक्ष सीमा है। MAXIMUM_WAIT_OBJECTS को फिर से परिभाषित करना या WaitForMultipleObjects के उपयोगकर्तालैंड पक्ष को फिर से कार्यान्वित करना बहुत कुछ नहीं करेगा। आपको बस इस विशेष सीमा के आसपास काम करने की जरूरत है। –