1

スレッドを使用して pop3 アカウントにアクセスし、メッセージを取得しています。正常に動作しますが、完了するまでアプリケーションがロックされます。ウィンドウを移動したり、シャットダウンしたり、ボタンをクリックしたりできません。

正常に動作し、コメントアウトした場所まで (または IdPOP31.Connect(); の後) メインアプリケーションにアクセスできます。

// サーバーが持っているメッセージの数を取得する

それからそれはロックアップします

procedure TPopThread.Pop;
var
 vName, vEmail, vServerIn, vServerOut, vUserId, vPassword: String;
 vPop3Port, vSMTPPort, vSSL: String; vHTML: TStringList;
   MsgCount : Integer;
   i,j        : Integer;
   FMailMessage :  TIdMessage;
begin
   with frmMain do
   begin
   RzMemo1.Lines.Clear;
   vHTML:= TStringList.Create;
   GetAccount(lbxMain.SelectedItem,vName, vEmail, vServerIn, vServerOut, vUserId, vPassword,
   vPop3Port, vSMTPPort, vSSL, vHTML);
   IdPOP31.Host      := vServerIn;
   IdPOP31.Username  := vUserId;
   IdPOP31.Password  := vPassword;
   IdPOP31.Port      := StrToInt(vPop3Port);

  try
     Prepare(IdPOP31);
     IdPOP31.Connect();
//   {
//   //Getting the number of the messages that server has.
//   MsgCount := IdPOP31.CheckMessages;
//   for i:= 0 to Pred(MsgCount) do
//   begin
//     try
//       FMailMessage := TIdMessage.Create(nil);
//       IdPOP31.Retrieve(i,FMailMessage);
//       RzMemo1.Lines.Add('=================================================');
//       RzMemo1.Lines.Add(FMailMessage.From.Address);
//       RzMemo1.Lines.Add(FMailMessage.Recipients.EMailAddresses);
//       RzMemo1.Lines.Add(FMailMessage.Subject);
//       RzMemo1.Lines.Add(FMailMessage.Sender.Address);
//       RzMemo1.Lines.Add(FMailMessage.Body.Text);
//
//       for J := 0 to Pred( FMailMessage.MessageParts.Count ) do
//       begin
//        // if the part is an attachment
//        if ( FMailMessage.MessageParts.Items[ J ] is TIdAttachment) then
//        begin
//         RzMemo1.Lines.Add('Attachment: ' + TIdAttachment(FMailMessage.MessageParts.Items[J]).Filename);
//        end;
//       end;
//       RzMemo1.Lines.Add('=================================================');
//     finally
//       FMailMessage.Free;
//     end;
//     RzMemo1.Clear;
//   end;
//   }
   finally
     IdPOP31.Disconnect;
     vHTML.Free;
   end;
   end;
end;

スレッドを追加する前に実際にこれを行ったので、スレッドではなくコメントアウトされた部分と関係があります

私は何を間違えましたか、またはしませんでしたか?

ここに私の実行があります

procedure TPopThread.Execute;
begin
  try
    Synchronize(Pop);
  except
    on Ex: Exception do
      fExceptionMessage := Ex.Message;
  end;
end;

これが私がそれを呼ぶ方法です

PopThread := TPopThread.Create(lbxMain.SelectedItem, frmMain.DonePopping);
4

2 に答える 2

4

pop メソッドの呼び出しを同期しているため、自分でアプリケーションをロックしています。

Synchronize を使用すると、 AMethod で指定された呼び出しがメイン スレッドを使用して実行されるため、マルチスレッドの競合が回避されます。

現在のスレッドは AThread パラメータで渡されます。

メソッド呼び出しがスレッド セーフかどうか不明な場合は、Synchronize メソッド内から呼び出して、メイン スレッドで確実に実行されるようにします。メソッドがメイン スレッドで実行されている間、現在のスレッドの実行は中断されます。

したがって、実際には、すべてのコードがメイン スレッドで実行されるため、余分なスレッドがないように見えます。

Synchronize を使用する場合の例は、VCL コンポーネントと対話する場合です。

一方、メソッドから多数のビジュアル コントロールに直接アクセスしており、VCL はスレッド セーフではないため、メイン スレッドでメソッドを実行する必要があります。

最善の方法は、スレッドから VCL コンポーネントにアクセスせずに、スレッドを VCL から独立させることです。代わりに、メモリ内のすべての入力値と出力値を収集し、スレッドの開始前と開始後にメイン スレッドから設定/読み取りを行います。スレッドが終了します。

または、何らかの理由でそれを行いたくない場合は、メソッドを分析して、VCL へのアクセスが必要な部分を分離し、その部分のみを同期することができます。次に例を示します。

type
  TPopThread = class
  private
    FMailMessage :  TIdMessage;  //now the message belongs to the class itself

  ...
  public
    //all the values are passed via constructor or the thread is 
    //created in suspended state, configured and then started
    property Host: string read FHost write FHost;
    property UserName: string read FUserName write FUserName;
    property Password: string read ...;
    property Port: Integer read ...;
  end;

procedure TPopThread.CopyMailToGUI;
var
  J: Integer;
begin
  frmMain.RzMemo1.Lines.Add('=================================================');
  frmMain.RzMemo1.Lines.Add(FMailMessage.From.Address);
  frmMain.RzMemo1.Lines.Add(FMailMessage.Recipients.EMailAddresses);
  frmMain.RzMemo1.Lines.Add(FMailMessage.Subject);
  frmMain.RzMemo1.Lines.Add(FMailMessage.Sender.Address);
  frmMain.RzMemo1.Lines.Add(FMailMessage.Body.Text);

  for J := 0 to Pred( FMailMessage.MessageParts.Count ) do
  begin
    // if the part is an attachment
    if ( FMailMessage.MessageParts.Items[ J ] is TIdAttachment) then
    begin
      frmMain.RzMemo1.Lines.Add('Attachment: ' + TIdAttachment(FMailMessage.MessageParts.Items[J]).Filename);
    end;
  end;
  frmMain.RzMemo1.Lines.Add('=================================================');
end;

procedure TPopThread.Pop;
var
  MsgCount : Integer;
  i,j        : Integer;
  Pop: TIdPOP3;
begin
  Pop := TIdPOP3.Create(nil);
  try
    Pop.Host      := FHost;
    Pop.Username  := FUserName;
    Pop.Password  := FPassword;
    Pop.Port      := FPort;
    Prepare(Pop);
    Pop.Connect();
    //Getting the number of the messages that server has.
    MsgCount := Pop.CheckMessages;
    for I := 0 to Pred(MsgCount) do
    begin
      try
        FMailMessage := TIdMessage.Create(nil);
        try
          IdPOP31.Retrieve(i,FMailMessage);
          Synchronize(CopyMailToGUI);
        finally
          FMailMessage.Free;
        end;
    end;
  finally
    Pop.Free;
  end;
end;

procedure TPopThread.Execute;
begin
  //no need of a try/except, if an exception occurs, it 
  //is stored in the FatalException property
  Pop;
end;

これで、スレッドはメイン スレッドに、処理されたメッセージだけを VCLにコピーするように要求します。そのコピー中、スレッドはブロックされ、アプリケーションはメインスレッドがビジーであるためメッセージに応答しませんが、それは非常に短い間隔であるため、理想的なケースではない場合でも、必要に応じて機能すると思います.

于 2013-02-28T15:51:33.250 に答える
0

すべてのロジックをSynchronize呼び出し内に配置します。Synchronizeメインの VCL スレッドでその機能を実行するため、そもそも別のスレッドを使用することで得られる可能性がある利点を本質的に無効にします。

への呼び出しを削除して、作成したスレッドで実行されるようにしますSynchronizePop

メインスレッドで実行する操作がまだ必要な場合は、それらをサブルーチンに入れて、 でのみ実行できるようにしますSynchronize。そのコードに表示される部分は、メモ コントロールに行を追加する場所です。

于 2013-02-28T15:50:07.043 に答える