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 方法中调用它,以确保它在主线程中执行。当方法在主线程中执行时,当前线程的执行被挂起。

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

当您想与 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。在该复制期间,您的线程将阻塞并且您的应用程序将不会响应消息,因为主线程很忙,但这将是非常短的时间间隔,所以即使它不是理想的情况,我认为它可以满足您的需求.

于 2013-02-28T15:51:33.250 回答
0

你把所有的逻辑都放在一个Synchronize调用中。Synchronize在主 VCL 线程中运行它的功能,因此您基本上已经取消了从一开始使用单独线程可能获得的任何好处。

删除对的调用,Synchronize以便Pop在您为其创建的线程中运行。

如果你还需要在主线程中执行一些操作,那就把它们放在子程序中,这样你就可以Synchronize. 我在该代码中看到的部分是您向备忘录控件添加行的地方。

于 2013-02-28T15:50:07.043 回答