मुझे यह रेमी का दिलचस्प कोड मिला है। डेल्फी : थ्रेड कैसे बनाएं और उपयोग करें स्थानीय रूप से?

क्या ऐसा किया जा सकता है ताकि मैं कई धागे कर सकूं और जब तक वे सभी समाप्त न हो जाएं तब तक प्रतीक्षा करें और फिर मुख्य धागे के साथ जारी रखें? मैंने इसे इस तरह से आजमाया लेकिन कोई सफलता नहीं मिली ...

procedure Requery(DataList: TStringList);
var
  Event: TEvent;
  H: THandle;
  OpResult: array of Boolean;
  i: Integer;
begin
  Event := TEvent.Create;
  try
    SetLength(OpResult, DataList.Count); 
    for i:=0 to DataList.Count-1 do begin
      TThread.CreateAnonymousThread(
        procedure
        begin
          try
            // run query in thread
            OpResult[i]:=IsMyValueOK(DataList.Strings[i]);
          finally
            Event.SetEvent;
          end;
        end
      ).Start;
      H := Event.Handle;
    end;
    while MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_ALLINPUT) = (WAIT_OBJECT_0+1) do Application.ProcessMessages;
    
    for i:=Low(OpResult) to High(OpResult) do begin
      Memo1.Lines.Add('Value is: ' + BoolToStr(OpResult[i], True));
    end;
  finally
    Event.Free;
  end;

  // Do next jobs with query
  ...
end;
4
davornik 7 अक्टूबर 2020, 22:46

1 उत्तर

सबसे बढ़िया उत्तर

क्या ऐसा किया जा सकता है ताकि मैं कई धागे कर सकूं और जब तक वे सभी समाप्त न हो जाएं तब तक प्रतीक्षा करें

हाँ। आपको बस कई TEvent ऑब्जेक्ट बनाने होंगे, प्रत्येक TThread के लिए एक, और फिर MsgWaitForMultipleObjects() को पास करने के लिए उनके सभी Handle को एक सरणी में संग्रहीत करना होगा:

procedure Requery(DataList: TStringList);
var
  Events: array of TEvent;
  H: array of THandle;
  OpResult: array of Boolean;
  i: Integer;
  Ret, Count: DWORD;

  // moved into a helper function so that the anonymous procedure
  // can capture the correct Index...
  procedure StartThread(Index: integer);
  begin
    Events[Index] := TEvent.Create;
    TThread.CreateAnonymousThread(
      procedure
      begin
        try
          // run query in thread
          OpResult[Index] := IsMyValueOK(DataList.Strings[Index]);
        finally
          Events[Index].SetEvent;
        end;
      end
    ).Start;
    H[Index] := Events[Index].Handle;
  end;

begin
  if DataList.Count > 0 then
  begin
    SetLength(Events, DataList.Count);
    SetLength(H, DataList.Count);
    SetLength(OpResult, DataList.Count);

    try
      for i := 0 to DataList.Count-1 do begin
        StartThread(i);
      end;

      Count := Length(H);
      repeat
        Ret := MsgWaitForMultipleObjects(Count, H[0], False, INFINITE, QS_ALLINPUT);
        if Ret = WAIT_FAILED then RaiseLastOSError;
        if Ret = (WAIT_OBJECT_0+Count) then
        begin
          Application.ProcessMessages;
          Continue;
        end;
        for i := Integer(Ret-WAIT_OBJECT_0)+1 to High(H) do begin
          H[i-1] := H[i];
        end;
        Dec(Count);
      until Count = 0;

      for i := Low(OpResult) to High(OpResult) do begin
        Memo1.Lines.Add('Value is: ' + BoolToStr(OpResult[i], True));
      end;
    finally
      for i := Low(Events) to High(Events) do begin
        Events[i].Free;
      end;
    end;
  end;

  // Do next jobs with query
  ...
end;

कहा जा रहा है, आप वैकल्पिक रूप से TEvent वस्तुओं से छुटकारा पा सकते हैं और इसके बजाय TThread.Handles पर प्रतीक्षा कर सकते हैं। जब थ्रेड पूरी तरह से समाप्त हो जाता है तो एक थ्रेड का Handle प्रतीक्षा ऑपरेशन के लिए संकेतित होता है। एकमात्र गच्चा यह है कि TThread.CreateAnonymousThread() एक TThread बनाता है जिसकी FreeOnTerminate संपत्ति True है, इसलिए आपको इसे मैन्युअल रूप से बंद करना होगा:

procedure Requery(DataList: TStringList);
var
  Threads: array of TThread;
  H: array of THandle;
  OpResult: array of Boolean;
  i: Integer;
  Ret, Count: DWORD;

  // moved into a helper function so that the anonymous procedure
  // can capture the correct Index...
  procedure StartThread(Index: integer);
  begin
    Threads[Index] := TThread.CreateAnonymousThread(
      procedure
      begin
        // run query in thread
        OpResult[Index] := IsMyValueOK(DataList.Strings[Index]);
      end
    );
    Threads[Index].FreeOnTerminate := False;
    H[Index] := Threads[Index].Handle;
    Threads[Index].Start;
  end;

begin
  try
    SetLength(Threads, DataList.Count);
    SetLength(H, DataList.Count);
    SetLength(OpResult, DataList.Count);

    for i := 0 to DataList.Count-1 do begin
      StartThread(i);
    end;

    Count := Length(H);
    repeat
      Ret := MsgWaitForMultipleObjects(Count, H[0], False, INFINITE, QS_ALLINPUT);
      if Ret = WAIT_FAILED then RaiseLastOSError;
      if Ret = (WAIT_OBJECT_0+Count) then
      begin
        Application.ProcessMessages;
        Continue;
      end;
      for i := Integer(Ret-WAIT_OBJECT_0)+1 to High(H) do begin
        H[i-1] := H[i];
      end;
      Dec(Count);
    until Count = 0;

    for i := Low(OpResult) to High(OpResult) do begin
      Memo1.Lines.Add('Value is: ' + BoolToStr(OpResult[i], True));
    end;
  finally
    for i := Low(Threads) to High(Threads) do begin
      Threads[i].Free;
    end;
  end;

  // Do next jobs with query
  ...
end;

किसी भी तरह से, ध्यान दें कि MsgWaitForMultipleObjects() एक बार में अधिकतम 63 (MAXIMUM_WAIT_OBJECTS[64] - 1) हैंडल पर प्रतीक्षा करने तक सीमित है। WaitForMultipleObjects() दस्तावेज़ीकरण बताता है कि उस सीमा के आसपास कैसे काम करना है, यदि आपको इसकी आवश्यकता है:

MAXIMUM_WAIT_OBJECTS से अधिक हैंडल पर प्रतीक्षा करने के लिए, निम्न विधियों में से एक का उपयोग करें:

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

या, आप बस अपनी सूची को छोटे बैचों में संसाधित कर सकते हैं, एक बार में 50-60 से अधिक आइटम न कहें।

5
Remy Lebeau 8 अक्टूबर 2020, 14:40