2011-11-08 9 views
1

私はDelphi 2007とスレッドを使用しています。イベントとシェア変数の作成

私の問題(申し訳ありませんが、私はより良い説明しようとするでしょう):

1)私は、私はより多くを使用する機能を持っているファイル「utilities.pas」を作成しました。 2)新しいプログラムを作成しました。このプログラムには1つのスレッドがあります。 3)スレッドの実行メソッドで、私のファイル "utilities.pas"に1つの関数を呼び出します。 この機能は、巧妙なコンポーネント(tclftp)を使用してftpに接続します。このコンポーネントは、専用イベントでサーバーの応答を記録します。私がしたいのは、文字列リストにログを保存してから、文字列リストを呼び出し元のスレッドに送り返すことです。

これは、ファイル "utilities.pas" の一部です:

// I created TEventHandlers because it's the only way to assign the event runtime 
// without having a class 
type 
TEventHandlers = class 
    procedure clFtp1SendCommand(Sender: TObject; const AText: string); 
end; 

var EvHandler: TEventHandlers; 

// this is the porcedure called from the thread. i want to send the stringlist 
// back to it containing the ftp log 
procedure Test(VAR slMain: tStringlist); 
var cFTP: TclFtp; 
begin 
cFTP := TclFtp.Create(nil); 

cFTP.Server := 'XXX'; 
cFTP.UserName := 'XXX'; 
cFTP.Password := 'XXX'; 
cFTP.OnSendCommand := EvHandler.clFtp1SendCommand; 

// i connect to the ftp 
cFTP.Open; 

FreeAndNil(cFTP); 
end; 

procedure TEventHandlers.clFtp1SendCommand(Sender: TObject; const AText: string); 
begin 
// here the component (cftp) sends me back the answer from the server. 
// i am logging it 

// HERE IT'S THE PROBLEM: 
// I can't reach slMain from here..... 

slmain.add(Atext); 
end; 

これは、呼び出し元のスレッドです:

procedure TCalcThread.Execute; 
var slMain: tstringlist; 
begin 
    inherited; 

    slmain := tstringlist.create(nil); 

    Test(slmain); 

    if slMain.count > 0 then 
    slMain.savetofile('c:\a.txt'); 

    // i won't free the list box now, but in the thread terminated. 
end; 

これは、メインプログラムである:

procedure TfMain.ThreadTerminated(Sender: TObject); 
Var ExThread: TCalcThread; 
begin 
    ExThread := (Sender as TCalcThread); 

    if ExThread.slMain.Count > 0 then 
    ExThread.slMain.SaveToFile('LOG\Errori.log'); 

freeandnil(slMain); 
end; 

してください誰でも私を助けてこれを解決できますか?私は本当に何をすべきか分からない。 私は今それをより明確に望みます。

P.S.すべての答えのおかげで。

+0

タイプミス:slMailは次のようになります。slMain –

+0

私はあなたがcFTP.Openを呼び出した後slMainで何かをする必要があると?もしそうなら、あなたはコメントを書くべきです。さもなければ、書かれているように、slMainは無関係です。 –

+0

プロシージャ "Test"はスレッド内で実行されますか?もしそうなら、それを明確にすべきです。 –

答えて

0

私は1つ(BAD)のアプローチは、メインスレッドまたは設計時にコンポーネントのプールを作成し、各スレッドに1つを割り当てることだと思います。 5つのcFTPインスタンス、5つの文字列リスト、5つのスレッドを含む。

更新:Martin Jamesはこれがひどい考えである理由を指摘し、私は同意します。そうしないでください。ポストは抑止力として残っている。

+1

ブレークカプセル化 - フォーム/メインスレッドはFTPコンポーネントにアクセスする必要はありません。各スレッドが独自のコンポーネントとリストを作成する場合、混乱が少なくなります。なぜ、メインスレッドはそれを行うべきですか?また、セカンダリスレッドで使用されるフォームにコンポーネントをダンプすると、AV/216/217例外を防ぐために、シャットダウン時にフォームが閉じられる前にセカンダリスレッドを終了する必要があります。あまりにも多くの "ここに私のスレッドをきれいにシャットダウンするにはどうすればいいですか?"という記事が既にあります - それが避けられれば避けてください! –

+0

合意。悪いアイデア。何をしないかを反映するための編集。 –

0

インターセプトイベントを TThreadStringList、そして:あなたはすべてが(おそらく各スレッドが達成内容の要約のため)への書き込みを行うことを一つの「マスタースレッド」を持っている必要がある場合は、このクラスを使用しますそのハンドラ内から型付きのイベントを発生させます。この呼び出しを同期してください!そして、グローバル変数を防ぐようにしてください。このすべては、次のように:

type 
    TFtpSendCommandEvent = procedure(Mail: TStrings; const AText: String) of object; 

    TMyThread = class(TThread) 
    private 
    FclFtp: TclFtp; 
    FslMail: TStrings; 
    FOnFtpSendCommand: TFtpSendCommandEvent; 
    FText: String; 
    procedure clFtpSendCommand(Sender: TObject; const AText: String); 
    procedure DoFtpSendCommand; 
    protected 
    procedure Execute; override; 
    public 
    // You could add this property as parameter to the constructor to prevent the 
    // need to assign it separately 
    property OnFtpSendCommand: TFtpSendCommandEvent read FOnFtpSendCommand 
     write FOnFtpSendCommand; 
    end; 

// If you dont want to make this a property or private field of the thread class: 
var 
    EvHandler: TFtpSendCommandEvent; 

{ TMyThread } 

procedure TMyThread.clFtpSendCommand(Sender: TObject; const AText: string); 
begin 
    // Store the AText parameter temporarily in a private field: Synchronize only 
    // takes a parameterless method 
    FText := AText; 
    Synchronize(DoFtpSendCommand); 
end; 

procedure TMyThread.DoFtpSendCommand; 
begin 
    if Assigned(FOnFtpSendCommand) then 
    FOnFtpSendCommand(FslMail, FText); 
    // Or, if you really like to use that global variable: 
    if Assigned(EvHandler) then 
    EvHandler(FslMail, FText); 
end; 

procedure TMyThread.Execute; 
begin 
    ... 
    FclFtp := TclFtp.Create(nil); 
    FslMail := TStringList.Create(nil); 
    try 
    FclFtp.Server := 'XXX'; 
    FclFtp.UserName := 'XXX'; 
    FclFtp.Password := 'XXX'; 
    FclFtp.OnSendCommand := clFtpSendCommand; 
    FclFtp.Open; 
    finally 
    FreeAndNil(FclFtp); 
    FreeAndNil(FslMail); 
    end; 
    ... 
end; 
+0

こんにちは、ありがとうございました。申し訳ありませんが、私は非常にうまく説明していませんが、機能(テスト)はトレッドに直接ありません。これはスレッドから呼び出され、 "utilities.pas"というファイルの一部です。だから私はこれを使うことはできないと思う。私はまた、より頻繁に使用するすべてのプロシージャを含む "utilities.pas"の中に大きなスレッドクラスを作成することを考えましたが、私はメインプログラムからそれらのすべてを呼び出す方法がわからないので停止しました。関数 "RunThread"ではなく、プロシージャを直接... – lorife