2012-09-04 75 views
0

我正在使用線程(再次),這總是痛苦的...我有一個函數在我的線程類,它是私人的。該函數返回一個布爾結果,以檢查某個POP3服務器是否有效。檢查工作正常,我測試了它,並且看起來像至少工作,但是在Thread.Execute過程中嘗試訪問結果時遇到問題..我會更多地解釋,讓我們繼續閱讀代碼..這裏是聲明:內部線程訪問函數結果

type 
    MyThread = class(TThread) 
    public 
    constructor Create(HostLine: string); 
    protected 
    procedure Execute; override; 
    procedure MainControl(Sender: TObject); 
    private 
    Host: string; 
    function CheckPOPHost: boolean; //this is the problematic function 
    end; 

因此函數是這樣的:

function MyThread.CheckPOPHost: boolean; 
var 
    MySocket: TClientSocket; 
    SockStream: TWinSocketStream; 
    Buffer: array[0..1023] of Char; 
    ReceivedText: string; 
begin 
    Result:= false; 
    FillChar(Buffer, SizeOf(Buffer), #0); 
    MySocket:= TClientSocket.Create(Nil); 
    MySocket.Port:= 110; 
    MySocket.ClientType:= ctBlocking; 
    MySocket.Host:= Host; 
    MySocket.Active:= true; 
    if (MySocket.Socket.Connected = true) then 
    begin 
     SockStream := TWinSocketStream.Create(MySocket.Socket, 1000); 
     SockStream.WaitForData(10000); 
     while (SockStream.Read(Buffer, SizeOf(Buffer)) <> 0) do 
     ReceivedText:= ReceivedText + Buffer; 
     if Length(ReceivedText) > 0 then 
     ReceivedText:= PAnsiChar(ReceivedText); 
     if AnsiStartsStr('+', ReceivedText) then 
     Result:= true; 
    end; 
    SockStream.Free; 
    MySocket.Free; 
end; 

所以,你並不需要閱讀,如果不想。但是,我連接到一些遠程主機,並接收它的文本,如果收到的文本以'+'符號(默認爲POP3服務器)開頭,我返回true ...但是當我嘗試在Thread.Execute內部執行此操作時:

if CheckPOPHost = true then 
    begin 
     Form1.Memo1.Lines.Append('Valid HOST:: '+Host); 

只是不行。我認爲這是很好的記憶,而不是如果裏面的功能,這樣做:

if AnsiStartsStr('+', ReceivedText) then 
    Result:= true; 

我做的:

if AnsiStartsStr('+', ReceivedText) then 
    Form1.Memo1.Lines.Append('Valid HOST:: '+Host); 

它正常工作......這是怎麼回事?

編輯::我得到錯誤'如果CheckPOPHost = true然後'行。由於某些未知原因,它提供了訪問衝突錯誤。

+0

在調試器下通過什麼來告訴你?你可以做到這一點。我們不可以。 –

+0

這是什麼PAnsiChar的事情?你爲什麼不使用AnsiString和AnsiChar? –

+0

使用'File-> New-> Other-> Thread'時,自動讀取由IDE放置的註釋。它特別告訴你**不**從你的線程訪問GUI控件而不使用'Synchronize'。請注意單詞*** not ***,這正是您在線程中使用「Form1.Memo1」時所忽略的內容。 –

回答

5

要調用

SockStream.Free 

即使你不輸入創建SockStream塊。這意味着您的代碼可以在未初始化的變量上調用Free。這是我可以查看訪問衝突的唯一原因。您需要移動if塊內的Free。

順便說一句,總是使用try /終於在創建對象時:

SockStream := TWinSocketStream.Create (MySocket.Socket, 1000); 
try 
    .... 
finally 
    SockStream.Free; 
end; 

有了你這樣寫它時,編譯器不會讓你把自由錯塊。

你應該對MySocket做同樣的事情。

並不從線程訪問GUI,但我認爲其他人已經明確表達了這一點!

+0

這是真正解決錯誤的唯一答案..其他人談論同步,這裏沒有問題... – HwTrap

+0

爲了對其他人公平,一旦發現明顯的錯誤,就很難看到它過去。但是,您報告的地址處的AV並不容易被GUI線程問題所解釋。 –

+0

@HwT - 根據你的接受,你甚至沒有連接('if'塊中的代碼沒有運行)。但是,在你的問題結尾,你說明了,而不是'Result:= true;'如果你在'if'塊中寫'Form1.Memo1.Lines ...'*,那麼就沒有錯誤。要麼你的問題是誤導/錯誤/廢話,或者你仍然有問題需要解決。 –

6

您正在調用線程的非線程安全VCL方法。

Form1.Memo1.Lines.Append('Valid HOST:: '+Host); 

不要這樣做。

將呼叫包裝在Synchronize(YourThread.SendString);方法在線程中。

例子:

MyThread.SendString; 
begin 
    Form1.Memo1.Lines.Append('Valid HOST:: '+Host); 
end; 

而且在某處你的線程執行:

if CheckPOPHost = true then 
    begin 
    Synchronize(SendString); 

更新2:

從你有關CheckPOPHost釋放對象的意見:附上圍繞這些try..finally塊對象和一個額外的try..except塊內喲你的CheckPopHost。

更新

約在評論線程嚮導的討論arised。 這是,如果你跟隨嚮導界面創建線程單元你會得到什麼:

unit Unit21; 

interface 

uses 
    System.Classes; 

type 
    TMYTHREADTEST = class(TThread) 
    private 
    { Private declarations } 
    protected 
    procedure Execute; override; 
    end; 

implementation 

{ 
    Important: Methods and properties of objects in visual components can only be 
    used in a method called using Synchronize, for example, 

     Synchronize(UpdateCaption); 

    and UpdateCaption could look like, 

    procedure TMYTHREADTEST.UpdateCaption; 
    begin 
     Form1.Caption := 'Updated in a thread'; 
    end; 

    or 

    Synchronize( 
     procedure 
     begin 
     Form1.Caption := 'Updated in thread via an anonymous method' 
     end 
    ) 
    ); 

    where an anonymous method is passed. 

    Similarly, the developer can call the Queue method with similar parameters as 
    above, instead passing another TThread class as the first parameter, putting 
    the calling thread in a queue with the other thread. 

} 

{ TMYTHREADTEST } 

procedure TMYTHREADTEST.Execute; 
begin 
    { Place thread code here } 
end; 

end. 
+0

+1。對我來說,'TThread'是(AFAIK)是IDE創建的唯一新單元,它在實現部分的開頭有一個大的註釋塊,告訴你使用'Synchronize',但沒有人能看到它。 :-)我的意思是,當線程單元第一次創建時它佔用了整個編輯器窗口,並且它在編輯器窗口中居中。 –

+0

是的,我知道,但我只是測試,並不認爲這是問題...一旦調試器出現'if CheckPOPHost = true'行錯誤... – HwTrap

+0

@HwTrap:調試器不會在這條線上有問題。編譯器可能,但調試器不會。是什麼讓你認爲它是調試器? –