2

我希望能够在单击按钮时终止线程,例如,如果用户愿意,可以中途停止进程。显然,您可以通过监视线程中的 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 还是会杀死它所在的线程?

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

4

1 回答 1

5

是的,在您的线程对象上调用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 的某些部分,则不应从线程代码中调用它。

于 2011-02-04T03:44:49.553 回答