首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >主应用程序使用idPop3检索邮件消息时被锁定(即使在线程中)

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

Stack Overflow用户
提问于 2013-02-28 22:52:23
回答 2查看 664关注 0票数 1

我正在使用一个线程来访问pop3帐户并检索邮件。它工作得很好,但它会锁定我的应用程序,直到它完成。不能移动窗口,关机,点击按钮,什么都不做。

它运行得很好,允许我访问主应用程序,直到我注释掉的地方(或者在IdPOP31.Connect();之后)

//获取服务器拥有的消息数

然后它就锁住了

代码语言:javascript
复制
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;

它实际上是在我添加线程之前完成的,所以它与被注释掉的部分有关,而不是线程

我做错了什么或没做什么?

下面是我的执行

代码语言:javascript
复制
procedure TPopThread.Execute;
begin
  try
    Synchronize(Pop);
  except
    on Ex: Exception do
      fExceptionMessage := Ex.Message;
  end;
end;

我是这样称呼它的

代码语言:javascript
复制
PopThread := TPopThread.Create(lbxMain.SelectedItem, frmMain.DonePopping);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-02-28 23:51:33

您将自己锁定应用程序,因为您正在synchronizing对pop方法的调用。

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

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

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

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

例如,当您想要与VCL组件进行交互时,就需要使用Synchronize

另一方面,因为您直接从方法访问许多可视化控件,而VCL不是线程安全的,所以您必须在主线程中执行您的方法。

最好的做法是使线程独立于VCL,方法是不从线程访问任何VCL组件,而是收集内存中的所有输入和输出值,并在线程开始之前和线程结束后从主线程中设置/读取这些值。

或者,如果出于任何原因您不想这样做,您可以剖析您的方法,以分离需要访问VCL的部分,并仅同步这些部分,例如:

代码语言:javascript
复制
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。在复制期间,您的线程将阻塞,您的应用程序将不会响应消息,因为主线程很忙,但这将是非常短的时间间隔,所以即使这不是理想的情况,我认为它将为您想要的工作。

票数 4
EN

Stack Overflow用户

发布于 2013-02-28 23:50:07

您可以将所有逻辑都放在Synchronize调用中。Synchronize在主VCL线程中运行它的函数,因此您实际上已经从一开始就取消了使用单独线程可能获得的任何好处。

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

如果您仍然需要在主线程中执行一些操作,那么可以将它们放在子例程中,以便只能在Synchronize中运行它们。我在这段代码中看到的部分是向memo控件添加行的地方。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15138529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档