创建事件并共享变量

时间:2011-11-08 13:17:46

标签: multithreading delphi events variables delphi-2007

我正在使用Delphi 2007和线程。

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

1)我创建了一个文件" utilities.pas"我有更多功能的地方。 2)我创建了一个新程序,在这个程序中我有一个线程 3)在线程的execute方法中,我在我的文件中调用了一个函数" 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。谢谢你的所有答案..

3 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

另一种方法是让你的线程对象拥有自己的stringlist实例和自己的cFTP。如果你需要一个所有写入的“主线程”(可能是每个线程完成的内容的摘要),请使用以下类: Tilo Eckert的TThreadStringList http://www.swissdelphicenter.ch/torry/showcode.php?id=2167

答案 2 :(得分: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;