Delphi 终止a';睡眠';线

Delphi 终止a';睡眠';线,delphi,delphi-xe2,Delphi,Delphi Xe2,我有一个任务,我想在后台运行,而不是中断GUI线程来检查新的程序版本 当应用程序启动时,它会立即将线程排队等待15秒,然后执行其余代码。如果在15秒结束前手动触发检查,则应终止现有的自动线程。如果应用程序在任何时候关闭,则应尽快终止所有剩余线程 在下面的示例中,我使用2分钟来简化调试 我现在的问题是,我尝试使用的WaitFor、Terminate、FreeOnTerminate和ontterminated组合都不会得到期望的结果。要么未调用Destroy,而我得到内存泄漏,要么在终止线程时应用程

我有一个任务,我想在后台运行,而不是中断GUI线程来检查新的程序版本

当应用程序启动时,它会立即将线程排队等待15秒,然后执行其余代码。如果在15秒结束前手动触发检查,则应终止现有的自动线程。如果应用程序在任何时候关闭,则应尽快终止所有剩余线程

在下面的示例中,我使用2分钟来简化调试

我现在的问题是,我尝试使用的
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;