Utilisation de TEvent et MsgWaitForMultipleObjects pour bloquer le thread principal
J'ai trouvé le code intéressant de Remy. Delphi: Comment créer et utiliser Thread localement?
Est-ce que cela peut être fait pour que je puisse faire plusieurs threads et attendre qu'ils soient tous terminés, puis continuer avec le thread principal? Je l'ai essayé comme ça mais sans succès ...
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;
Réponses
Est-ce que cela peut être fait pour que je puisse faire plusieurs threads et attendre qu'ils soient tous terminés
Oui. Vous devez simplement créer plusieurs TEventobjets, un pour chacun TThread, puis stocker tous leurs Handles dans un tableau pour les transmettre à MsgWaitForMultipleObjects():
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;
Cela étant dit, vous pouvez également vous débarrasser des TEventobjets et attendre le TThread.Handles à la place. Un thread Handleest signalé pour une opération d'attente lorsque le thread est complètement terminé. Le seul problème est que TThread.CreateAnonymousThread()crée une propriété TThreaddont FreeOnTerminateest True, vous devrez donc la désactiver manuellement:
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;
Dans tous les cas, notez que cela MsgWaitForMultipleObjects()se limite à attendre un maximum de 63 ( MAXIMUM_WAIT_OBJECTS[64] - 1) poignées à la fois. La WaitForMultipleObjects()documentation explique comment contourner cette limitation, si vous devez:
Pour attendre plus de MAXIMUM_WAIT_OBJECTS handles, utilisez l'une des méthodes suivantes:
- Créez un thread pour attendre sur les poignées MAXIMUM_WAIT_OBJECTS, puis attendez sur ce thread plus les autres poignées. Utilisez cette technique pour diviser les poignées en groupes de MAXIMUM_WAIT_OBJECTS.
- Appelez RegisterWaitForSingleObject pour attendre chaque handle. Un thread d'attente du pool de threads attend les objets enregistrés MAXIMUM_WAIT_OBJECTS et affecte un thread de travail une fois que l'objet est signalé ou que l'intervalle de temporisation a expiré.
Ou, vous pouvez simplement traiter votre liste par lots plus petits, par exemple pas plus de 50 à 60 éléments à la fois.