2011-11-08 42 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