为什么我得到异常参数超出范围? [英] Why i get an exception argument out of range?

查看:107
本文介绍了为什么我得到异常参数超出范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以解释一下为什么我在执行以下代码时有时会在ios模拟器下收到异常参数超出范围的原因吗?在android上,我永远不会收到任何错误。我使用Delphi berlin。

Can someone can explain me why i receive sometime an Exception "Argument out of range" under the ios simulator when i execute the code below? on android i never get any error. I use Delphi berlin.

出现错误的函数:

{**********************************************************************}
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)则行得通:

Just to explain a little better, remember it's only on ios it's not work, and if i add a sleep(1000) after the fSignal.resetEvent then it's work :

    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;

so it's look like that the signal is not set to OFF just after doing fSignal.ResetEvent;

我认为这是TEvent或Tmonitor中的错误:(

i m affraid it's a bug in TEvent or Tmonitor :(

推荐答案

您的池使用的是自动重置事件,而应该使用的是手动重置事件。您不希望每个等待操作在 Dequeue()可以在池中仍有线程时重置事件,当 any 项在池中时应发出信号,而在池为空时则不发出信号

Your pool is using an auto-reset event when it should be using a manual-reset event instead. You don't want each wait operation in Dequeue() to reset the event while there are still threads in the pool. It should be signaled while any item is in the pool, and unsignaled while the pool is empty. And your constructor is not signaling the event after adding your initial threads to the pool so they can be dequeued layer.

Dequeue()一样,构造函数也不会在将初始线程添加到池中后向事件发出信号,以便可以将它们出队。 本身比应该的要复杂一些,它可以简化为以下类似内容:

As for Dequeue() itself, is a bit more complicated than it should be. It can be simplified to something more like the following instead:

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;

我还注意到的是 fPool TObjectList< T> OwnsObjects 属性设置为false的情况,这违背了使用<$ c的目的$ c> TObjectList 。您也可以使用 TList< T> 代替。实际上,使用 fPool 的方式应该使用 TStack< T>

Something else I notice is that fPool is a TObjectList<T> with its OwnsObjects property set to false, which kind of defeats the purpose of using TObjectList. You may as well just use TList<T> instead. And actually, the way you are using fPool, you should use TStack<T> instead. It would clean up your code a little more and make it that much easier to read and understand.

尝试以下操作:

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;

这篇关于为什么我得到异常参数超出范围?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆