我正在使用线程访问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);
答案 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
中仅运行 。我在该代码中看到的部分是您在备忘录控件中添加行的位置。