2017-08-31 10 views
4

状況。私は、このクラスには時間がかかる可能性があり、Executeメソッドを持っているのでTThreadを使用することを決定したDelphi TThread子孫リターン結果

type 
TCongrError = class(Exception) 
end; 

type 
TCongruence = class(TComponent) 
    //code stuff 
    constructor Create(a, b, n: integer); virtual; 
end; 

type 
TCongrSystem = array of TCongruence; 

type 
TCongruenceSystem = class(TThread) 
    private 
    resInner: integer; 
    FData: TCongrSystem; 
    function modinv(u, v: integer): integer; //not relevant 
    protected 
    procedure Execute; override; 
    public 
    constructor Create(data: TCongrSystem; var result: integer; hasClass: boolean); 
end; 

:私は代数のもの(合同式とシステムを)解決するためにいくつかのクラスでユニットを作成している、私はあなたのコードを示していますコンストラクタに渡されるパラメータの長さのために終了します。ここでは実装です:

constructor TCongruenceSystem.Create(data: TCongrSystem; var result: integer; hasClass: boolean); 
begin 

inherited Create(True); 
FreeOnTerminate := true; 

FData := data; 
setClass := hasClass; 
resInner := result; 

end; 

procedure TCongruenceSystem.Execute; 
var sysResult, i, n, t: integer; 
begin 

sysResult := 0; 
n := 1; 

//computation 

Queue(procedure 
     begin 
     ShowMessage('r = ' + sysResult.ToString); 
     resInner := sysResult; 
     end); 

end; 

PROBLEM

あなたはQueueを見ればあなたは、私が(ちょうどテストとして)ShowMessageを使用していますが、それはsysResultの正しい値を示していることがわかります。途中で2番目の行には、私が理解できないいくつかの問題があります。

コンストラクタはvar result: integerです。したがって、渡された変数から副作用があり、次にresInner := result;を割り当てることができます。最後(キュー内)にはresInnerにsysResultの値が与えられており、varという副作用のためにresultも更新されると予想しています。なぜこれは起こりませんか?

私はこのようなコンストラクタを変更し、別のテスト作っています、私は参照して、[OKであるTMemoオブジェクトを渡していますコンストラクタで

Queue(procedure 
     begin 
     ShowMessage('r = ' + sysResult.ToString); 
     resInner.Lines.Add(sysResult.ToString); 
     end); //this code now works properly in both cases! (showmessage and memo) 

constructor TCongruenceSystem.Create(data: TCongrSystem; result: TMemo; hasClass: boolean); 
//now of course I have resInner: TMemo 

そして、これにキューを変更します元のものではありませんvar result: integerあまりにも参考として渡されますか?なぜそれは機能しませんか?

私はこのような何かしたいと思いますので、私はこれをしたい:giveMeSolutionは、システムの結果を含む変数aを使用して単純な手順である

//I put var a: integer; inside the public part of the TForm 
test := TCongruenceSystem.Create(..., a, true); 
test.OnTerminate := giveMeSolution; 
test.Start; 
test := nil; 

を。これができない場合、私は何ができますか?基本的にExecuteの最後の結果は、メインスレッドに渡されなければならない整数です。

私はReturnValueについて読んだことがありますが、使用方法がわかりません。

+0

これはひどい設計されており、あなたは遅かれ早かれよりもそれを修正する必要があります。確かにここではTComponentから派生するものはありません。それは無意味です。より大きな問題は、コンポーネントにスレッドを構築することです。それはコードの消費者によって処理されるべきです。あなたのコメントのために@DavidHeffernanありがとう –

+0

。私はこれでコンポーネントを作りたかったのです。あなたの提案を読んでください私はスレッドのものを削除し、単にシステムが処理するクラス機能を追加するべきだと思います。 –

+1

コンポーネントは無意味です。これは数値クラスです。デザイン面には必要ありません。 –

答えて

3

基本的Executeの端部における結果は、メインスレッドに渡さなければならないだけの整数です。

私はReturnValueについて読んだことがありますが、使用方法はわかりません。ReturnValueプロパティを使用して

は非常に簡単です:

type 
    TCongruenceSystem = class(TThread) 
    ... 
    protected 
    procedure Execute; override; 
    public 
    property ReturnValue; // protected by default 
    end; 

procedure TCongruenceSystem.Execute; 
var 
... 
begin 
    // computation 
    ReturnValue := ...; 
end; 

test := TCongruenceSystem.Create(...); 
test.OnTerminate := giveMeSolution; 
test.Start; 

.... 

procedure TMyForm.giveMeSolution(Sender: TObject); 
var 
    Result: Integer; 
begin 
    Result := TCongruenceSystem(Sender).ReturnValue; 
    ... 
end; 
+0

ありがとう、これは私が探していたものです。戻り値は整数のみを受け入れますか?あるいは、例えばレコードも? –

+0

@AlbertoMiola整数のみです。より複雑なデータを返す場合は、私の答えに示されているようなカスタムイベントが最適です。 –

+0

@AlbertoMiola:または少なくともスレッドクラスのカスタムフィールド/プロパティを使用します。 –

3

クラスフィールドをFFoo : integer;としましょう。あなたは何をしているか

ここ
procedure TFoo.Foo(var x : integer); 
begin 
    FFoo := x; 
end; 

FFooxを割り当てることです。メソッドFooの中では、渡された変数の値をxとして自由に変更できますが、integersは割り当て時にコピーされる値の型です。外部integer変数への参照を保持する場合は、FFoo(または、場合によってはresInner)をPInteger(整数へのポインタ)として宣言する必要があります。例えば、(単純化):それはTMemoで動作する理由は、クラスは参照型であるということです

{ ** See the bottom of this answer for why NOT to use } 
{ Queue with FreeOnTerminate = true **    } 
Queue(procedure 
    begin 
     ShowMessage('r = ' + sysResult.ToString); 
     resInner^ := sysResult; 
    end); 

- :

TCongruenceSystem = class(TThread) 
    private 
    resInner: PInteger;  
    protected 
    procedure Execute; override; 
    public 
    constructor Create(result: PInteger); 
end; 

どこにtest := TCongruenceSystem.Create(@a);として呼び出して割り当てます

constructor TCongruenceSystem.Create(result: PInteger); 
begin  
    inherited Create(True); 
    FreeOnTerminate := true;  
    resInner := result;  
end; 

それらの変数は値を保持するのではなく、メモリ内のオブジェクトのアドレスを指し示します。クラス変数をコピーするときは、参照をコピーするだけです(つまりポインタ)。値型の場合は、変数の内容が割り当て時にコピーされます。言ったことで


、そこにあなたのコンストラクタで参照を取っvar x : integerとして型指定された引数を維持するからあなたを止めるものは何もありません:

constructor TCongruenceSystem.Create(var result: Integer); 
begin  
    inherited Create(True); 
    FreeOnTerminate := true;  
    resInner := @result; {take the reference here} 
end; 

が、これは、発信者にその一回のコンストラクタ印象を与えますあなたが意図している変数に何らかの変更を加えたもので、整数を自由に処分できるということは完全です。 PIntegerを明示的に渡すと、呼び出し元は、オブジェクトが提供する整数への参照を保持し、クラスが生存している間は基底の変数が有効であることを確認する必要があるというヒントを得ます。

...すべてのことを言って、私はまだ根本的にこの考えが嫌いです。このような可変参照を取り入れることで、呼び出し元に非定型の生涯管理の問題が発生します。ポインタの受け渡しは、転送ポイントでのみ使用される場所で行うのが最適です。外国のポインターをつかむのは面倒で、間違いが起こりやすいです。ここでははるかに良いアプローチは、完了イベントを提供し、クラスの消費者にハンドラを添付させることです。たとえばについては

{ define a suitable callback signature } 
    TOnCalcComplete = procedure(AResult : integer) of object; 

    TCongruenceSystem = class(TThread) 
    private 
    Fx, Fy : integer; 
    FOnCalcComplete : TOnCalcComplete; 
    protected 
    procedure Execute; override; 
    public 
    constructor Create(x,y: integer); 
    property OnCalcComplete : TOnCalcComplete read FOnCalcComplete write FOnCalcComplete; 
    end; 

constructor TCongruenceSystem.Create(x: Integer; y: Integer); 
begin 
    inherited Create(true); 
    FreeOnTerminate := true; 
    Fx := x; 
    Fy := y; 
end; 

procedure TCongruenceSystem.Execute; 
var 
    sumOfxy : integer; 
begin 
    sumOfxy := Fx + Fy; 
    sleep(3000);  {take some time...} 
    if Assigned(FOnCalcComplete) then 
    Synchronize(procedure 
       begin 
        FOnCalcComplete(sumOfxy); 
       end); 
end; 

あなたはその後、として呼び出すことになる:

{ implement an event handler ... } 
procedure TForm1.CalcComplete(AResult: Integer); 
begin 
    ShowMessage(IntToStr(AResult)); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    LCongruenceSystem : TCongruenceSystem; 
begin 
    LCongruenceSystem := TCongruenceSystem.Create(5, 2); 
    LCongruenceSystem.OnCalcComplete := CalcComplete; { attach the handler } 
    LCongruenceSystem.Start; 
end; 

また、私はここにSynchronizeの代わりQueueを使用していることに気づくでしょう。このトピックでは、この質問の読み取りを持ってください(私はレミーを...引用します):

Ensure all TThread.Queue methods complete before thread self-destructs

がFreeOnTerminateの設定:キューに入れられたメソッドで= Trueのメモリリークを求めています。

+0

ありがとう、私は私の推測する私の間違いを理解している!私はvarと私は値と参照だけでなく、値をコピーすることができたと思ったもちろん、私は間違っていた。 –