0

私は(再び)スレッドを使用していますが、これは常に苦痛です...スレッドクラス内に1つの関数があり、それはプライベートです。この関数は、1つのブール結果を返し、一部の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の場合」の行でエラーが発生します。なんらかの理由で、アクセス違反エラーが発生しています。

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:

オブジェクトの解放に関するコメントからCheckPOPHost:これらのオブジェクトの周囲のブロックと、CheckPopHost内try..finallyの追加のブロックを囲みます。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.
于 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 に答える