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);

它工作正常......发生了什么?!

编辑:: 我在 'if CheckPOPHost = true then' 行出现错误。由于某些未知原因,它给出了访问冲突错误。

4

2 回答 2

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:

根据您关于在 中释放对象的评论CheckPOPHosttry..finally在这些对象周围加上块,并try..except在 CheckPopHost 内附加一个块。

更新

评论中出现了关于线程向导的讨论。如果您按照向导界面创建线程单元,这就是您所得到的:

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.
于 2012-09-04T20:22:37.637 回答
5

你在打电话

SockStream.Free

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

顺便说一句,在创建对象时始终使用 try/finally:

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

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

你应该对 MySocket 做同样的事情。

并且不要从线程访问您的 GUI,但我认为其他人已经清楚地表明了这一点!

于 2012-09-04T22:45:28.793 回答