2016-10-05 19 views
1

以下のコードを実行すると、なぜiosシミュレータの下で例外「引数が範囲外です」が表示されるのですか?アンドロイドには何のエラーもありません。私はデルファイベルリンを使用しています。例外の引数が範囲外になるのはなぜですか?

エラーが表示される機能:完全なソースコードの下

{**********************************************************************} 
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; 

注:

ほんの少し良く説明するために、それだけで、それはのイオスにあります覚えていますfSignal.resetEventの後にスリープ(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のバグだaffraid :(それは代わりにマニュアルリセットイベントを使用しなければならないとき、あなたのプールは自動リセットイベントを使用している

+1

は、エンキューおよびデキューを使用しないでください。名前はプッシュとポップです。 –

答えて

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; 

何かがfPoolは一種のTObjectListを使用しての目的に反しfalseに設定されたOwnsObjectsプロパティとTObjectList<T>であるということです。代わりに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; 
+0

おかげであなたの非常に良い説明のためのレミー。しかし、それは私の実装がうまくいかなかった理由を説明していません:(あなたのメソッドは動作していますが、fSignal.WaitFor(Infinite)のためではなく、繰り返しのためだけです... fSignal.ResetEventを設定した後、fSignalがまだOFF状態になっていることがわかります。fSignal.ResetEventの直後にスリープ(1000)した場合、信号はONになります – loki

+0

レミー私はちょうどfSignal.ResetEvent; – loki

+0

の後に睡眠を追加してノートを追加しました。私はバグレポートを追加しました:https://quality.embarcadero.com/browse/RSP-16033 – loki

関連する問題