Delphi 终止a';睡眠';线
我有一个任务,我想在后台运行,而不是中断GUI线程来检查新的程序版本 当应用程序启动时,它会立即将线程排队等待15秒,然后执行其余代码。如果在15秒结束前手动触发检查,则应终止现有的自动线程。如果应用程序在任何时候关闭,则应尽快终止所有剩余线程 在下面的示例中,我使用2分钟来简化调试 我现在的问题是,我尝试使用的Delphi 终止a';睡眠';线,delphi,delphi-xe2,Delphi,Delphi Xe2,我有一个任务,我想在后台运行,而不是中断GUI线程来检查新的程序版本 当应用程序启动时,它会立即将线程排队等待15秒,然后执行其余代码。如果在15秒结束前手动触发检查,则应终止现有的自动线程。如果应用程序在任何时候关闭,则应尽快终止所有剩余线程 在下面的示例中,我使用2分钟来简化调试 我现在的问题是,我尝试使用的WaitFor、Terminate、FreeOnTerminate和ontterminated组合都不会得到期望的结果。要么未调用Destroy,而我得到内存泄漏,要么在终止线程时应用程
WaitFor
、Terminate
、FreeOnTerminate
和ontterminated
组合都不会得到期望的结果。要么未调用Destroy
,而我得到内存泄漏,要么在终止线程时应用程序挂起,要么我得到无法终止外部创建的线程
异常
线程代码
unituncheckthread;
接口
使用SysUtils、类、SyncObjs和对话框;
类型
TCheckThread=class(TThread)
私有的
FDelayEvent:TEvent;
FDelay:整数;
公众的
构造函数创建(const ADelay:Integer);
毁灭者毁灭;推翻
程序执行;推翻
程序终止集;推翻
结束;
实施
{TCheckThread}
构造函数TCheckThread.Create(constadelay:Integer);
开始
继承创建(True);
FreeOnTerminate:=False;
FDelay:=ADelay;
FDelayEvent:=TEvent.Create(nil,True,False,”);
结束;
析构函数TCheckThread.Destroy;
开始
FDelayEvent.Free;
继承;
结束;
程序TCheckThread.Execute;
开始
FDelayEvent.WaitFor(msecpersec*FDelay);
{如果另一个线程在等待延迟时进行了检查,请取消此检查}
如果终止,则退出;
{一些长时间运行的代码}
睡眠(10000);
如果终止,则退出;
同步化(
程序()
开始
MessageDlg('threadcompleted',mtConfirmation,[mbOK],0);
(完),;
结束;
程序TCheckThread.TerminatedSet;
开始
FDelayEvent.SetEvent;
结束;
结束。
用户界面
单元1;
接口
使用
Winapi.Windows、Winapi.Messages、System.SysUtils、System.Variants、System.Classes、Vcl.Graphics、,
控件、窗体、对话框、取消选中线程、Vcl.StdCtrls;
类型
TForm1=类(TForm)
按钮1:t按钮;
按钮2:t按钮;
程序按钮1点击(发送方:ToObject);
程序按钮2点击(发送者:对象);
过程FormClose(发送方:ToObject;var操作:TCloseAction);
过程表单创建(发送方:ToObject);
私有的
{私有声明}
AThread:TCheckThread;
终止程序(发送方:TObject);
公众的
{公开声明}
结束;
变量
表1:TForm1;
实施
{$R*.dfm}
程序TForm1.按钮1单击(发送方:TObject);
开始
如果已分配(AThread),则开始
终止;
//AThread.WaitFor;/?
结束;
AThread:=TCheckThread.Create(0);//立即开始
AThread.OnTerminate:=OnTerminate;
AThread.开始;
结束;
程序TForm1.按钮2单击(发送方:TObject);
开始
接近;
结束;
过程TForm1.FormClose(发送方:TObject;var操作:TCloseAction);
开始
如果已分配(AThread),则开始
终止;
//AThread.WaitFor;/??
结束;
结束;
过程TForm1.FormCreate(发送方:TObject);
开始
AThread:=tcheckshread.Create(SecsPerMin*2);//等待2分钟后再开始
结束;
程序TForm1.onterminate(发送方:TObject);
开始
FreeAndNil(AThread);
结束;
结束。
您不能中断睡眠()
,因此这对于调试来说不是一个好的选择,特别是对于长时间间隔。您应该改用FDelayEvent.WaitFor()
。这样,线程可以在终止时快速“唤醒”
另外,不要在Execute()
的末尾调用Synchronize()
,而应该在线程完成时使用OnTerminate
事件来执行所需的任何操作OnTerminate
已与主UI线程同步
例如,当线程终止时,可以使用OnTerminate
将AThread
变量设置为nil
,以便Assigned(AThread)
检查不会失败。您已经尝试这样做了,但是您不能安全地从“代码> >终止/ <代码>事件处理程序中的代码<代码> Fabue/Cux>线程,因此您可以考虑使用<代码> FrunEntoTale= Trime< /Cord>,或者至少延迟<代码>免费<代码>,直到处理程序退出。
就这点而言,我不建议在应用程序启动时首先创建一个延迟线程。改用t定时器
。当触发其OnTimer
事件时,创建一个非延迟线程。如果用户触发手动检查,只需禁用计时器。这样,您就不会浪费资源创建一个闲置的线程,也不必担心多个线程之间的同步问题
话虽如此,请尝试以下方式:
unituncheckthread;
接口
使用
班级;
类型
TCheckThread=class(TThread)
受保护的
程序执行;推翻
公众的
构造函数创建(aInterminate:TNotifyEvent);
结束;
实施
使用
SysUtils;
{TCheckThread}
构造函数TCheckThread.Create(aInterminate:TNotifyEvent);
开始
继承创建(False);
FreeOnTerminate:=False;
OnTerminate:=a终止;
结束;
程序TCheckThread.Execute;
变量
I:整数;
开始
{一些长时间运行的代码}
对于I:=1到20 do
开始
如果终止,则退出;
睡眠(500);
结束;
结束;
结束。
如果:
- 无论出于何种原因,您都希望保留代码的结构
- 当您说“如果应用程序在任何时候关闭,则应尽快终止所有剩余线程。”->这意味着我们并不真正关心线程运行/处理任何进程的结果…
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, unCheckThread;
const
WM_FREE_THREAD = WM_APP + 1;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Timer1: TTimer;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
AThread: TCheckThread;
procedure ThreadFinished(Sender: TObject);
procedure StartThread;
procedure StopThread;
procedure WMFreeThread(var Message: TMessage); message WM_FREE_THREAD;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
StartThread;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Interval := 120000; // wait for 2 minutes before starting
Timer1.Enabled = True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
StopThread;
end;
procedure TForm1.StartThread;
begin
Timer1.Enabled := False;
if not Assigned(AThread) then
begin
AThread := TCheckThread.Create(ThreadFinished);
Button1.Enabled := False;
end;
end;
procedure TForm1.StopThread;
begin
if Assigned(AThread) then
begin
AThread.OnTerminate := nil;
AThread.Terminate;
AThread.WaitFor;
FreeAndNil(AThread);
end;
end;
procedure TForm1.ThreadFinished(Sender: TObject);
begin
AThread := nil;
// in 10.2 Tokyo and later, you can use TThread.ForceQueue() instead...
// TThread.ForceQueue(nil, Sender.Free);
PostMessage(Handle, WM_FREE_THREAD, 0, LPARAM(Sender));
MessageDlg('Thread completed', mtConfirmation, [mbOK], 0);
Button1.Enabled := True;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
StartThread;
end;
procedure TForm1.WMFreeThread(var Message: TMessage);
begin
TObject(Message.LParam).Free;
end;
end.
if Assigned(AThread) then begin
TerminateThread(AThread.Handle, 0);
FreeAndNil(AThread);
end;