在线程内访问函数结果

时间:2012-09-04 20:13:15

标签: delphi delphi-2010

我正在使用Threads(再次),这总是很痛苦...我的线程类中有一个函数,它是私有的。该函数返回一个布尔结果,以检查某个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然后'行时收到错误。由于某些未知原因,它会出现访问冲突错误。

2 个答案:

答案 0 :(得分: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块,并在CheckPopHost中附加try..except块。

<强>更新

出现了关于评论中的线程向导的讨论。 如果您在向导界面后创建一个线程单元,那么这就是您所获得的:

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.

答案 1 :(得分:5)

您正在致电

SockStream.Free

即使您没有输入创建SockStream的块。这意味着您的代码可以在未初始化的变量上调用Free。这是我可以看到访问冲突的唯一原因。你需要在if块中移动Free。

另外,在创建对象时始终使用try / finally:

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

如果你这样编写,编译器就不会让你把Free放在错误的块中。

你应该对MySocket做同样的事。

并且不要从线程中访问GUI,但我认为其他人已经明确指出了这一点!