2011-02-04 3 views
2

Я хочу, чтобы можно было прекратить поток нажатием кнопки, например, остановить процесс на полпути, если пользователь хочет. По-видимому, вы можете сделать это, помазав за конечную переменную в потоке, а это значит, что вы можете затем выпустить некоторый код перед выходом, а не внезапно завершать.Завершить тему, запустив код перед выходом

код до сих пор выглядит следующим образом: -

резьб по щелчку

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; 

Actual Thread

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(), однако я не могу точно узнать, что это делает. Устанавливает ли оно значение «Завершение» на «Истина» или просто убивает поток, где он стоит?

P.S. Я не размещал код из синхронизированных подпрограмм. Один печатает в StringGrid, один обновляет ProgressBar, а третий устанавливает переменную op, которая используется подпрограммой StringGrid Sync, и я не вижу, чтобы этот код имел отношение к проблеме, однако я могу отправить сообщение по запросу.

+0

's/divress/ramble on непрестанно /' :-) – paxdiablo

+5

После прочтения документации для 'TThread.Terminate', где говорится:« Terminate устанавливает для свойства Terminated потока значение true, сигнализируя, что поток должен быть прерван как можно скорее насколько это возможно ", почему вы продолжаете задаваться вопросом, что он делает? Кроме того, помните, что у вас есть исходный код. Если вы хотите знать, что он делает, откройте * Classes.pas * и посмотрите. –

+0

Те вызовы Synchronize могут быть невероятно дорогостоящими. Почему бы вам просто не записать информацию в целое число, убедитесь, что она выровнена на границе 4 байта, и получите ваш основной поток, чтобы периодически его читать по таймеру и обновлять интерфейс таким образом. Вы потеряете все преимущества потоковой передачи, если потратите время на синхронизацию потоков! –

ответ

5

Да, позвоните по номеру Terminate на свой объект темы. Он устанавливает Terminated в true, и это все, что вам нужно - ваш поток должен проверить на Terminated, как ваш код делает (хотя есть ошибка, см. Ниже.)

Подробнее: вы создаете свой объект потока здесь:

cmpfiles := TCompareFilesThread.Create(); 

Сделать cmpfiles переменную-член вашей формы или другого класса, не являющиеся локальными для Panel29Click процедуры. Инициализируйте его до nil в конструкторе. Затем, когда вам нужно отменить, звоните:

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

Это устанавливает флаг «Завершение», если поток существует. (Редактировать: спасибо Робу, который предложил избегать FreeOnTerminate. Это был мой плохой совет, чтобы предложить его использовать в первую очередь.) Ваш поток также должен уведомить ваш основной поток, когда он закончен, прямо в конце Execute, чтобы вы может освободить его. Один из способов сделать это - использовать Synchronize для уведомления основного потока, а затем в основном потоке метода, который вы передаете Synchronize, вы можете освободить объект нити.

FreeAndNil(cmpfiles); 

ли это в самом конце из Execute, поскольку объект потока будет удален - вы не хотите больше кода для запуска.

Одна ошибка, я заметил: Ваш Execute код имеет следующие два, пока петли:

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, вы не должны вызывать его из вашего кода потока.

+0

Спасибо за помощь :) Хорошее место на ошибках тоже. Например, первая ошибка была непроверенным кодом, поэтому я буду работать над этим. Второе из первоначально полученных данных из формы, однако было обновлено, чтобы не использовать форму, но я просто не смог ее удалить из класса;) – jskrwyk

+0

Пожалуйста, не устанавливайте 'FreeOnTerminate: = True', а затем продолжать ссылаться на объект извне класса потока. В любой момент ссылка может стать недействительной. Это свойство «установить и забыть», поэтому, если вы не можете его забыть, не устанавливайте его. –

+0

Да ... если это удастся без отмены, cmpfiles будет недействительным. TheFlatline, а не FreeOnTerminate, ваш поток уведомляет основной поток, когда он один, и освобождает его (и сводит на нет вашу переменную). Таким образом, вы знаете, что cmpfiles всегда действителен. –

Смежные вопросы