为什么我的异常参数超出范围?

时间:2016-10-05 22:12:27

标签: delphi firemonkey

当我执行下面的代码时,有人可以解释我为什么我会在ios模拟器下收到异常“超出范围的参数”的原因吗?在android上我永远不会得到任何错误。我使用德尔福柏林。

出现错误的函数:

{**********************************************************************}
procedure Twin_WorkerThreadPool.Enqueue(const Value: Twin_WorkerThread);
begin
  Tmonitor.Enter(fPool);
  try
    fPool.Add(Value);
    fSignal.SetEvent;
  finally
    Tmonitor.Exit(fPool);
  end;
end;

{********************************************************}
function Twin_WorkerThreadPool.Dequeue: Twin_WorkerThread;
begin
  Tmonitor.Enter(self); // << only one thread can execute the code below
  try

    Tmonitor.Enter(fPool);
    try
      if Fpool.Count > 0 then begin
        result := fPool[Fpool.Count - 1];
        fPool.Delete(Fpool.Count - 1);
        exit;
      end;
      fSignal.ResetEvent;
    finally
      Tmonitor.Exit(fPool);
    end;

    fSignal.WaitFor(Infinite);

    Tmonitor.Enter(fPool);
    try
      result := fPool[Fpool.Count - 1]; // << exception argument out of range ? but how it's possible ?
      fPool.Delete(Fpool.Count - 1);
    finally
      Tmonitor.Exit(fPool);
    end;

  finally
    Tmonitor.exit(self);
  end;
end;

完整源代码下方:

  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
  Twin_WorkerThreadPool = class(TObject)
  private
    fPool: TObjectList<Twin_WorkerThread>;
    fSignal: Tevent;
  public
    procedure Enqueue(const Value: Twin_WorkerThread);
    function Dequeue: Twin_WorkerThread;
  end;

{***********************************}
constructor Twin_WorkerThread.Create;
begin
  FProc := nil;
  FProcReadySignal := TEvent.Create(nil, false{ManualReset}, false, '');
  FProcFinishedSignal := TEvent.Create(nil, false{ManualReset}, false, '');
  inherited Create(False); // see http://www.gerixsoft.com/blog/delphi/fixing-symbol-resume-deprecated-warning-delphi-2010
end;

{***********************************}
destructor Twin_WorkerThread.Destroy;
begin
  Terminate;
  FProcReadySignal.setevent;
  WaitFor;
  FProcReadySignal.Free;
  FProcFinishedSignal.Free;
  inherited;
end;

{**********************************}
procedure Twin_WorkerThread.Execute;
begin
  while True do begin
    try

      //wait the signal
      FProcReadySignal.WaitFor(INFINITE);

      //if terminated then exit
      if Terminated then Break;

      //execute fProc
      if assigned(FProc) then FProc();

      //signal the proc is finished
      FProcFinishedSignal.SetEvent;

    except
      //hide the exception
    end;
  end;
end;

{**********************************************************}
procedure Twin_WorkerThread.ExecuteProc(const AProc: TProc);
begin
  fProc := AProc;
  FProcFinishedSignal.ResetEvent;
  FProcReadySignal.SetEvent;
end;

{*****************************************************************}
procedure Twin_WorkerThread.ExecuteAndWaitProc(const AProc: TProc);
begin
  fProc := AProc;
  FProcFinishedSignal.ResetEvent;
  FProcReadySignal.SetEvent;
  FProcFinishedSignal.WaitFor(INFINITE);
end;

{********************************************************************}
constructor Twin_WorkerThreadPool.Create(const aThreadCount: integer);
var i: integer;
begin
  fPool := TObjectList<Twin_WorkerThread>.create(false{aOwnObjects});
  fSignal := TEvent.Create(nil, false{ManualReset}, false, '');
  for I := 0 to aThreadCount - 1 do
    fPool.Add(Twin_WorkerThread.Create)
end;

{***************************************}
destructor Twin_WorkerThreadPool.Destroy;
var i: integer;
begin
  for I := 0 to fPool.Count - 1 do begin
    fPool[i].disposeOf;
    fPool[i] := nil;
  end;
  fPool.Free;
  fSignal.Free;
  inherited Destroy;
end;

{*********************************************************************}
procedure Twin_WorkerThreadPool.ExecuteAndWaitProc(const AProc: TProc);
var aThread: Twin_WorkerThread;
begin
  aThread := Dequeue;
  try
    aThread.ExecuteAndWaitProc(aProc);
  finally
    Enqueue(aThread);
  end;
end;

注:

只是为了解释一下,记住它只在ios上它不起作用,如果我在fSignal.resetEvent之后添加一个sleep(1000)那么它的工作:

    Tmonitor.Enter(fPool);
    try
      if Fpool.Count > 0 then begin
        result := fPool[Fpool.Count - 1];
        fPool.Delete(Fpool.Count - 1);
        exit;
      end;
      fSignal.ResetEvent;

      sleep(1000);   

    finally
      Tmonitor.Exit(fPool);
    end;

    fSignal.WaitFor(Infinite);

所以看起来在执行fSignal.ResetEvent之后信号没有设置为OFF;

我担心这是TEvent或Tmonitor的错误:(

1 个答案:

答案 0 :(得分:1)

您的游泳池正在使用自动重置事件,而应该使用手动重置事件。您不希望Dequeue()中的每个等待操作都重置事件,而池中仍有线程。它应该在任何项目在池中时发出信号,并且在池为空时无信号。并且在将初始线程添加到池中之后,构造函数不会发出事件信号,因此它们可以是出列的层。

至于Dequeue()本身,有点复杂。它可以简化为更像以下内容:

procedure Twin_WorkerThreadPool.Enqueue(const Value: Twin_WorkerThread);
begin
  TMonitor.Enter(fPool);
  try
    fPool.Add(Value);
    if fPool.Count = 1 then
      fSignal.SetEvent;
  finally
    TMonitor.Exit(fPool);
  end;
end;

function Twin_WorkerThreadPool.Dequeue: Twin_WorkerThread;
begin
  repeat
    TMonitor.Enter(fPool);
    try
      if fPool.Count > 0 then begin
        Result := fPool[fPool.Count - 1];
        fPool.Delete(fPool.Count - 1);
        if fPool.Count = 0 then
          fSignal.ResetEvent;
        Exit;
      end;
    finally
      TMonitor.Exit(fPool);
    end;
    fSignal.WaitFor(Infinite);
  until False;
end;

我注意到的另一件事是fPoolTObjectList<T>,其OwnsObjects属性设置为false,这种方法违背了使用TObjectList的目的。您也可以使用TList<T>代替。实际上,您使用fPool的方式,应该使用TStack<T>。它会更清理你的代码并使其更容易阅读和理解。

请改为尝试:

type
  Twin_WorkerThreadPool = class(TObject)
  private
    fPool: TStack<Twin_WorkerThread>;
    fSignal: Tevent;
  public
    constructor Create(const aThreadCount: integer);
    destructor Destroy; override;
    procedure Enqueue(const Value: Twin_WorkerThread);
    function Dequeue: Twin_WorkerThread;
  end;

constructor Twin_WorkerThreadPool.Create(const aThreadCount: integer);
var
 i: integer;
begin
  inherited Create;
  fPool := TStack<Twin_WorkerThread>.Create;
  fSignal := TEvent.Create(nil, True{ManualReset}, False, '');
  for I := 0 to aThreadCount - 1 do
    fPool.Add(Twin_WorkerThread.Create);
  if fPool.Count > 0 then
    fPool.SetEvent;
end;

destructor Twin_WorkerThreadPool.Destroy;
var
  i: integer;
begin
  for I := fPool.Count - 1 downto 0 do
    fPool.Pop.DisposeOf;
  fPool.Free;
  fSignal.Free;
  inherited Destroy;
end;

procedure Twin_WorkerThreadPool.ExecuteAndWaitProc(const AProc: TProc);
var
  aThread: Twin_WorkerThread;
begin
  aThread := Dequeue;
  try
    aThread.ExecuteAndWaitProc(aProc);
  finally
    Enqueue(aThread);
  end;
end;

procedure Twin_WorkerThreadPool.Enqueue(const Value: Twin_WorkerThread);
begin
  TMonitor.Enter(fPool);
  try
    fPool.Push(Value);
    if fPool.Count = 1 then
      fSignal.SetEvent;
  finally
    TMonitor.Exit(fPool);
  end;
end;

function Twin_WorkerThreadPool.Dequeue: Twin_WorkerThread;
begin
  repeat
    TMonitor.Enter(fPool);
    try
      if fPool.Count > 0 then begin
        Result := fPool.Pop;
        if fPool.Count = 0 then
          fSignal.ResetEvent;
        Exit;
      end;
    finally
      TMonitor.Exit(fPool);
    end;
    fSignal.WaitFor(Infinite);
  until False;
end;