【问题标题】:TThread.WaitFor on multiple consecutive threads多个连续线程上的 TThread.WaitFor
【发布时间】:2015-01-09 16:31:59
【问题描述】:

创建了一个线程 (TThreadStarter),它会依次创建多个工作线程,并且必须等待它们完成才能继续。它必须在 Linux 和 Windows 下都能运行。

下面的方法似乎是一个解决方案。但这是一个好的吗?你会采取不同的方式吗?

1) 创建所有线程并将其放入一个数组中,然后遍历该数组并调用 Start。

2) 然后简单地再次遍历数组并调用 WaitFor 以便执行停止 对于调用线程,直到所有线程都完成。

procedure TThreadStarter.Execute;
var i1:integer;
begin
  for i1:=1 to WORK_THREAD_COUNT do ThreadArray[i1].Start;
  for i1:=1 to WORK_THREAD_COUNT do ThreadArray[i1].WaitFor;

  ..Do some work

end;

我使用 Lazarus 和 FreePascal。

【问题讨论】:

  • 是的。这是正确的方法。
  • 在 Windows 上,使用允许您在多个句柄上等待的 Win32 API 函数之一更有效。
  • 调用WaitFor 有效,但可能不是最佳的。你有三种情况:最好的情况——最短的线程在前,最长的线程在后。中间情况 - 所有线程消耗的时间大致相同。最坏的情况 - 最长的线程在前,最短的线程在后。实际上,差异是微不足道的。正如大卫已经说过的那样,完美的解决方案是使用WaitForMultipleObjects
  • @René,那么你有类似的 Linux 解决方案吗?如果是这样,那么您找到了最佳解决方案。而且,我怀疑您甚至会注意到在循环中等待单个对象和在使用合理数量的线程时等待多个对象之间的性能差异。哪个线程最后完成也没关系。无论如何,它可以是第一个。您的循环将在此处中断并等待其完成,一旦完成,其余迭代中询问的其余线程将立即返回它们的WaitFor 方法调用。
  • @TLama 这很重要。等待涉及转换到内核模式。 63 次不必要的转换(最坏的情况)不好。很容易抽象出平台变化,以利用 Windows 上更丰富的线程原语。

标签: multithreading delphi freepascal lazarus


【解决方案1】:

等待(几乎)任意数量的工作线程的解决方案(仅限 Windows)。

从您的MainWorkerThread(不是普通的主线程)调用WaitForThreads 函数,并传入一个包含您要等待的所有线程句柄的数组。

interface

uses
  Windows, SysUtils, Classes;

type
  THandleDynArray = array of THandle;

  TMaximum_Wait_Objects_Handles = array[0..MAXIMUM_WAIT_OBJECTS - 1] of THandle;

  EMaximum_Handles_Exceeded = class(Exception);

  TThreadWaitForThreads = class(TThread)
  strict private
    FarrHandle: TMaximum_Wait_Objects_Handles;
    FThreadWaitCount: 0..MAXIMUM_WAIT_OBJECTS;
  protected
    procedure Execute; override;
  public
    procedure AddHandle(Handle: THandle); overload;
    procedure AddHandle(HandleArray: THandleDynArray); overload;
    procedure ClearHandles;
  end;

procedure WaitForThreads(const Handles: THandleDynArray);

implementation

procedure WaitForThreads(const Handles: THandleDynArray);
var
  Wait_Result: cardinal;
  arrHandle: TMaximum_Wait_Objects_Handles;
  arrWaitThreads: array[0..MAXIMUM_WAIT_OBJECTS - 1] of TThreadWaitForThreads;
  WaitThreadsCount, i: integer;
begin
  if Length(Handles) <= 0 then
    exit;    

  if Length(Handles) > MAXIMUM_WAIT_OBJECTS then
  begin
    WaitThreadsCount := Length(Handles) div MAXIMUM_WAIT_OBJECTS;

    for i := 0 to WaitThreadsCount - 1 do
    begin
      arrWaitThreads[i] := TThreadWaitForThreads.Create(true);
      arrHandle[i] := arrWaitThreads[i].Handle;

      arrWaitThreads[i].AddHandle(Copy(Handles, MAXIMUM_WAIT_OBJECTS * i, MAXIMUM_WAIT_OBJECTS));
    end;

    try
      if Length(Handles) mod MAXIMUM_WAIT_OBJECTS > 0 then
        Move(Handles[MAXIMUM_WAIT_OBJECTS * WaitThreadsCount], arrHandle[WaitThreadsCount], (Length(Handles) - WaitThreadsCount * MAXIMUM_WAIT_OBJECTS) * SizeOf(THandle));

      for i := 0 to WaitThreadsCount - 1 do
        arrWaitThreads[i].Resume;

      repeat
        Wait_Result := WaitForMultipleObjectsEx(WaitThreadsCount + Length(Handles) - WaitThreadsCount * MAXIMUM_WAIT_OBJECTS, @arrHandle, true, 10, true);
        Application.ProcessMessages;
      until ((Wait_Result <> WAIT_TIMEOUT)
        and (Wait_Result <> WAIT_IO_COMPLETION));

    finally
      for i := 0 to WaitThreadsCount - 1 do
      begin
        arrWaitThreads[i].Terminate; // terminating all WaitThreads to be on the safe side
        arrWaitThreads[i].Free;
      end;
    end;
  end
  else
  begin
    repeat
      Wait_Result := WaitForMultipleObjectsEx(Length(Handles), @Handles[0], true, 10, true);
      Application.ProcessMessages;
    until ((Wait_Result <> WAIT_TIMEOUT)
      and (Wait_Result <> WAIT_IO_COMPLETION));
  end;
end;

{ TThreadWaitForThreads }

procedure TThreadWaitForThreads.AddHandle(Handle: THandle);
begin
  if FThreadWaitCount < MAXIMUM_WAIT_OBJECTS then
  begin
    FarrHandle[FThreadWaitCount] := Handle;
    Inc(FThreadWaitCount);
  end
  else
    raise EMaximum_Handles_Exceeded.Create('You can only wait for ' + IntToStr(MAXIMUM_WAIT_OBJECTS) + ' Threads!');
end;

procedure TThreadWaitForThreads.AddHandle(HandleArray: THandleDynArray);
begin
  if FThreadWaitCount + Length(HandleArray) <= MAXIMUM_WAIT_OBJECTS then
  begin
    Move(HandleArray[0], FarrHandle[FThreadWaitCount], SizeOf(THandle) * Length(HandleArray));
    Inc(FThreadWaitCount, Length(HandleArray));
  end
  else
    raise EMaximum_Handles_Exceeded.Create('You want to wait for ' + IntToStr(FThreadWaitCount + Length(HandleArray)) + ' threads, but you can only wait for ' + IntToStr(MAXIMUM_WAIT_OBJECTS) + '!');
end;

procedure TThreadWaitForThreads.ClearHandles;
begin
  FThreadWaitCount := 0;
  ZeroMemory(@FarrHandle, SizeOf(FarrHandle));
end;

procedure TThreadWaitForThreads.Execute;
var
  Wait_Result: cardinal;
begin
  if FThreadWaitCount = 0 then
    exit;

  repeat
    Wait_Result := WaitForMultipleObjectsEx(FThreadWaitCount, @FarrHandle, true, 10, true);
  until ((Wait_Result <> WAIT_TIMEOUT)
    and (Wait_Result <> WAIT_IO_COMPLETION))
    or Terminated;

  ClearHandles;
end;

end.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 2013-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-26
    相关资源
    最近更新 更多