主应用程序使用idPop3锁定以检索邮件消息(即使在线程中)

时间:2013-02-28 14:52:23

标签: multithreading delphi delphi-2010

我正在使用线程访问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);

2 个答案:

答案 0 :(得分:4)

自己锁定应用程序,因为你synchronizing调用了pop方法。

  

Synchronize导致AMethod指定的调用使用主线程执行,从而避免多线程冲突。

     

当前线程在AThread参数中传递。

     

如果您不确定方法调用是否是线程安全的,请从Synchronize方法中调用它以确保它在主线程中执行。   当方法在主线程中执行时,当前线程的执行被暂停。

因此,出于实际目的,您就像没有额外的线程一样,因为所有代码都在主线程中执行。

  

当您想要与VCL组件交互

时,您希望使用Synchronize的示例

另一方面,因为您正在从方法中直接访问许多可视控件,并且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。在 copy 期间,你的线程会阻塞,你的应用程序将不会响应消息,因为主线程很忙,但这将是非常短暂的间隔,所以即使它不是理想的情况,我认为它会适合你想要的东西。

答案 1 :(得分:0)

您将所有逻辑放入Synchronize来电。 Synchronize在主VCL线程中运行其功能,因此您基本上无效使用首先使用单独线程可能获得的任何好处。

移除对Synchronize的调用,以便Pop在您为其创建的主题中运行。

如果您仍需要在主线程中执行某些操作,请将它们放在子例程中,以便您可以在Synchronize中仅运行 。我在该代码中看到的部分是您在备忘录控件中添加行的位置。