2016-09-07 10 views
11

निम्नलिखित परीक्षण कार्यक्रम में प्रत्येक टेस्ट थ्रेड अपने हैंडल को ग्लोबल 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. 

उत्तर

21

WaitForMultipleObjects()documentation कहता है:

वस्तु की अधिकतम संख्या संभालती MAXIMUM_WAIT_OBJECTS है।

MAXIMUM_WAIT_OBJECTS का मूल्य 64 (winnt.h में परिभाषित) है, इसलिए 100 हैंडल सीमा से अधिक है। हालांकि, प्रलेखन भी बताते हैं कि कैसे उस सीमा से उबरने के लिए:,

MAXIMUM_WAIT_OBJECTS हैंडल से ज्यादा पर इंतजार करने के लिए निम्न विधियों का उपयोग करें:

  • MAXIMUM_WAIT_OBJECTS हैंडल पर प्रतीक्षा करने के लिए एक धागा बनाएं, फिर उस धागे और अन्य हैंडल पर प्रतीक्षा करें। MAXIMUM_WAIT_OBJECTS के समूहों में हैंडल तोड़ने के लिए इस तकनीक का उपयोग करें।

  • प्रत्येक हैंडल पर प्रतीक्षा करने के लिए RegisterWaitForSingleObject पर कॉल करें। थ्रेड पूल से प्रतीक्षा थ्रेड MAXIMUM_WAIT_OBJECTS पंजीकृत ऑब्जेक्ट्स पर प्रतीक्षा करता है और ऑब्जेक्ट सिग्नल होने के बाद वर्कर थ्रेड असाइन करता है या टाइम-आउट अंतराल समाप्त हो जाता है।

पहली तकनीक का एक उदाहरण के लिए this question का जवाब देखें।

+3

[इस दृष्टिकोण] (http://tondrej.blogspot.com/2016/03/the-strange-limitation-of-64-threads.html) की विविधता का उपयोग करने की संभावना भी है: इंटरलॉक-वृद्धि/कम करने के लिए काउंटर और एक भी घटना की प्रतीक्षा की। –

+0

संपादन के लिए धन्यवाद। मुझे अपने से बेहतर जवाब के लिए थोड़ा बुरा क्रेडिट लगता है :-) –

+1

यह भी ध्यान दें कि 64 ऑब्जेक्ट सीमा वास्तव में एक कर्नेल पक्ष सीमा है। MAXIMUM_WAIT_OBJECTS को फिर से परिभाषित करना या WaitForMultipleObjects के उपयोगकर्तालैंड पक्ष को फिर से कार्यान्वित करना बहुत कुछ नहीं करेगा। आपको बस इस विशेष सीमा के आसपास काम करने की जरूरत है। –

संबंधित मुद्दे