【问题标题】:Terminate A Thread, Running Code Before Exit终止线程,退出前运行代码
【发布时间】:2011-06-21 02:57:48
【问题描述】:

我希望能够在单击按钮时终止线程,例如,如果用户愿意,可以中途停止进程。显然,您可以通过监视线程中的 Terminated 变量来做到这一点,这意味着您可以在退出之前执行一些代码,而不是突然终止。

目前的代码如下:-

点击开始线程

procedure TForm1.Panel29Click(Sender: TObject);
var
cmpfil : TThread;

begin
  if (Edit3.Text <> '') AND (Edit4.Text <> '') then
  begin
    Form1.ProgressBar1.Min := 0;
    Form1.Progressbar1.Max := 30000;
    Form1.ProgressBar1.Position := 0;
    cmpfiles := TCompareFilesThread.Create();
  end;
end; 

创建线程

constructor TCompareFilesThread.Create;
begin
  inherited Create(False);
end;

实际线程

procedure TCompareFilesThread.Execute;
var
  forg, fpat : file;
  byteorg, bytepat : Array[0..1023] of byte;
  i,z,o : integer;
  fil1,fil2 : TFilename;
begin
  //Form1.CompareFiles(FEdit3Text, FEdit4Text, FGrid, FOp, FProg);

  begin
    fil1 := Form1.Edit3.Text;
    fil2 := Form1.Edit4.Text;
    if Form1.CRCAdlerGenFile(fil1,1) <> Form1.CRCAdlerGenFile(fil2,1) then //Only Run if files arn't same
    begin
      op := 3;
      synchronize(SetOP);

      i := 0;
      x := 1;
      o := 0;

      AssignFile(forg,fil1);
      FileMode := fmOpenRead;
      Reset(forg,1);
      AssignFile(fpat,fil2);
      FileMode := fmOpenRead;
      Reset(fpat,1);

      //Set Progress Bar

      while NOT eof(forg) do
      begin
        while Terminated = False do
        begin
          BlockRead(forg,byteorg,1024);
          BlockRead(fpat,bytepat,1024);

          for z := 0 to 1023 do
          begin
            if byteorg[z] <> bytepat[z] then
            begin
              synchronize(sProgBarNext);
              by := bytepat[z];
              off := IntToStr(o);
              synchronize(SyncGrid);
              inc(x);
            end;
            inc(o);
          end;
        end;
      end;

      CloseFile(forg);
      CloseFile(fpat);
    end;
  end;
  Free;
end;

我已经添加了While Terminated = False do 行,它会在更改时停止进程。我似乎无法弄清楚如何改变它。我从未创建过该变量;这是内置的 Delphi 功能。我已经阅读了有关TMyThread.Terminate() 的信息,但我似乎无法确切了解它的作用。它将 Terminated 设置为 True 还是会杀死它所在的线程?

附:我还没有发布同步例程中的代码。一个打印到 StringGrid,一个更新 ProgressBar,第三个设置一个由 StringGrid Sync 例程使用的 op 变量,我认为此代码与问题无关,但是如果需要,我可以发布。

【问题讨论】:

  • s/digress/ramble on incessantly/ :-)
  • 在阅读了TThread.Terminate 的文档后,上面写着“Terminate 将线程的 Terminated 属性设置为 true,表示线程应该尽快终止”,为什么你还想知道什么?是吗?另外,请记住您有源代码。如果您想知道它的作用,请打开 Classes.pas 并查看。
  • 那些对 Synchronize 的调用可能会非常昂贵。为什么不直接将信息写入一个整数,确保它在 4 字节边界上对齐,并让你的主线程在计时器上定期读取它并以这种方式更新 UI。如果您花时间在线程同步上,您将失去线程的所有好处!
  • @Rob Kennedy 我已经阅读了该文档,这就是产生混淆的地方。我在这里发现了另一个问题,它也谈到了它,但它应该TThread.Terminate 会立即杀死线程,所以我认为我最好检查一下以确保安全。 @David Heffernan 好主意,谢谢 :)
  • 还有哪个问题告诉你TThread.Terminate 会杀死线程?你确定它说的是TThread.Terminate,而不是TerminateThread?后者是 Windows API 函数,它立即杀死一个线程。它们是完全不同的函数,它们的调用方式也不同。

标签: multithreading delphi terminate


【解决方案1】:

是的,在你的线程对象上调用Terminate。它将 Terminated 设置为 true,仅此而已 - 您的线程需要检查 Terminated,就像您的代码正在执行的操作一样(尽管存在错误,请参见下文。)

更详细地说:你在这里创建你的线程对象:

cmpfiles := TCompareFilesThread.Create();

使cmpfiles 成为您的表单或其他类的成员变量,而不是您的Panel29Click 过程的本地变量。在构造函数中将其初始化为nil。然后当你需要取消时,调用:

if Assigned(cmpfiles) then begin
  cmpfiles.Terminate;
end;

如果线程存在,这将设置终止标志。 (编辑:感谢 Rob,他建议避免使用 FreeOnTerminate。首先建议使用它是我的坏建议。)您的线程还需要在完成时通知您的主线程,就在执行结束时,以便您可以释放它。一种方法是使用Synchronize 通知主线程,然后在主线程中传递给Synchronize 的方法可以释放线程对象。

FreeAndNil(cmpfiles);

Execute 的最后执行此操作,因为线程对象将被删除 - 您不想再运行任何代码。

我发现了一个错误:您的 Execute 代码有以下两个 while 循环:

while NOT eof(forg) do
begin
   while Terminated = False do
      begin
         ...
      end;
end;

这实际上会在未终止的情况下循环,并且仅在将Terminated 设置为true 时检查它是否位于文件末尾。您可能希望循环同时检查两个条件,例如:

while (not eof(forg) and not Terminated) do
begin
   ...
end;

哦,还有另一个错误,您的线程代码访问 Form1 的数据:

if Form1.CRCAdlerGenFile(fil1,1) <> Form1.CRCAdlerGenFile(fil2,1) then //Only Run if files arn't same

这段代码是什么,对文件运行 CRC?如果CRCAdlerGenFile 函数实际上并没有触及 Form1 的任何部分并且它只依赖于它的参数(并且它们是独立的),则将其设置为某个辅助函数 - 它不必是类的一部分,你可以有独立的功能。如果它确实依赖于 Form1 的某些部分,则不应从线程代码中调用它。

【讨论】:

  • 感谢您的帮助 :) 错误的好地方。例如,第一个错误是未经测试的代码,因此我将对此进行处理。第二个最初是从表单访问数据,但是后来更新为不使用表单,但是我还没有把它从类中删除;)
  • 请不要设置FreeOnTerminate := True,然后继续从线程类外部引用对象。在任何时候,引用都可能变得无效。它是“设置它并忘记它”的属性,所以如果你不能忘记它,请不要设置它。
  • 是的...如果它没有取消就成功了,cmpfiles 将是无效的。 TheFlatline,而不是 FreeOnTerminate 让您的线程在主线程为一个时通知主线程,并在那里释放它(并使您的变量无效)。这样你就知道 cmpfiles 总是有效的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-18
  • 1970-01-01
  • 2015-04-11
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多