2016-10-05 168 views
1

有人可以解釋我爲什麼當我執行下面的代碼時,有時會在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後添加一個睡眠(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;

IM affraid它在TEvent或Tmonitor :(

+1

當你實現一個堆棧不要使用入隊和出隊。名字應該是Push和Pop。 –

回答

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; 
+0

感謝雷米對你很好的解釋。然而這並不能解釋爲什麼我的實現不工作:(你的方法正在工作,但僅僅是因爲重複...直到而不是因爲fSignal.WaitFor(無限);因爲如果你看得更仔細如果我在fSignal.ResetEvent之後做了睡眠(1000),那麼信號現在是 – loki

+0

remy,你可以看到這個錯誤是在設置fSignal.ResetEvent之後;然後fSignal仍然處於OFF狀態。我只是在fSignal.ResetEvent之後加入睡眠補充說明; – loki

+0

我剛剛添加了一個錯誤報告:https://quality.embarcadero.com/browse/RSP-16033 – loki

相關問題