2011-11-08 40 views
1

我正在使用Delphi 2007和线程。创建事件和共享变量

我的问题(对不起,我会试着解释更好):

1)我创建了一个文件“utilities.pas”在那里我有我使用更多的功能。 2)我创建了一个新程序,在这个程序中我有一个线程 3)在线程的执行方法中,我在文件“utilities.pas”中调用一个函数。 这个函数使用聪明的组件(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

我认为一个(BAD)的方法将是在主线程或在设计时创建一个组件池,并为每个线程分配一个。即5个cFTP实例,5个字符串列表,5个线程。

更新:马丁詹姆斯指出为什么这是一个可怕的想法,我同意。所以不要这样做。邮政仍然是一种威慑力量。

+1

中断封装 - 表单/主线程不需要访问FTP组件。如果每个线程都创建它自己的组件和列表,那么混乱就会少得多 - 主线程为什么要这样做?此外,将组件转储到由辅助线程使用的表单上,要求用户在关闭表单时终止辅助线程,以防止AV/216/217异常。在这里已经有太多的“我怎么干净地关闭我的线程”的帖子 - 如果可以避免,避免它! –

+0

同意。馊主意。编辑以反映不该做的事情。 –

0

另一种方法是让你的线程对象有自己的stringlist实例和自己的cFTP。如果你需要有一个“主线程”,一切都写入(可能为每个线程来完成内容的摘要),使用这个类: TThreadStringList通过的Tilo埃克特 http://www.swissdelphicenter.ch/torry/showcode.php?id=2167

0

拦截事件的线程类中,和从该处理程序中激发自己的类型化事件。同步此调用!并尝试阻止全局变量。所有这一切如下:

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