| 
| 
 | Вопрос # 6 119/ вопрос решён / | 
 |  Здравствуйте!Подскажите, пожалуйста как завершить поток во время работы при закрытии формы.
 К вопросу прикреплён файл. Загрузить » (срок хранения: 60 дней с момента отправки вопроса) Приложение:Переключить в обычный режим {{code|delphi}}...var  Form1: TForm1;  DownLoader_Thread: TDownLoader_Thread;  CriticalSection: TCriticalSection; implementation {$R *.dfm} procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);var  ThreadDieCode: Integer;begin   DownLoader_Thread.Terminate;    ThreadDieCode := DownLoader_Thread.WaitFor;    DownLoader_Thread.Free;   {SLRepairFiles.Free;  CriticalSection.Free;}end; procedure TForm1.FormCreate(Sender: TObject);begin  SLRepairFiles := TStringList.Create;  CriticalSection := TCriticalSection.Create;   SLRepairFiles.Add('Files\1.bmp');  SLRepairFiles.Add('Files\1.docx');  SLRepairFiles.Add('Files\1.mp4');  SLRepairFiles.Add('Files\1.rar');  SLRepairFiles.Add('Files\1.txt');end; procedure TForm1.Button1Click(Sender: TObject);begin  ProgressBar1.Max := SLRepairFiles.Count;   DownLoader_Thread := TDownLoader_Thread.Create(True);  DownLoader_Thread.FSLRepairFiles := SLRepairFiles;  DownLoader_Thread.FreeOnTerminate := True;  DownLoader_Thread.Resume;end; procedure TDownLoader_Thread.Execute;var  HTTP: TIdHTTP;  FS: TFileStream;  i: Integer;  Folder: string;begin  HTTP := TIdHTTP.Create(nil);   CriticalSection.Enter;   for i := 0 to FSLRepairFiles.Count - 1 do  begin      if Self.Terminated then Break;     Folder := ExtractFilePath(Application.ExeName) + ExtractFilePath(FSLRepairFiles.Strings[i]);    ForceDirectories(Folder);    try      try        FS := TFileStream.Create(Folder + ExtractFileName(FSLRepairFiles.Strings[i]), fmCreate);        HTTP.Get('http://77.108.194.247/' + StringReplace(FSLRepairFiles.Strings[i], '\', '/',
[rfReplaceAll, rfIgnoreCase]), FS);      except      end;    finally      FS.Free;    end;     Progress := i + 1;     Synchronize(SyncProc);  end;   CriticalSection.Leave;   HTTP.Free;end; procedure TDownLoader_Thread.SyncProc;begin  Form1.ProgressBar1.Position := Progress; end;{{code}} {{code|delphi}}ThreadDieCode := DownLoader_Thread.WaitFor;{{code}} {{code|delphi}}if Self.Terminated then Break;{{code}} 
|  |   Вопрос задал: Shouldercannon (статус: Посетитель)Вопрос отправлен: 7 мая 2012, 10:45
 Состояние вопроса: решён, ответов: 1.
 |  Ответ #1. Отвечает эксперт: Вадим К Здравствуйте, Shouldercannon!Есть два способа - правильное завершение и завершение с извращением. Второй способ подразумевает убивания потока, но при этом убивают его "зверски" и никто не гарантирует, что основная программа выживет. Поэтому этот способ не рекомендуется. (выглядит он так
 TerminateThread(имя_потока.ThreadId);) 
 Правильный способ заключается в том, что поток должен сам завершить себя. Для этого у делфовской реализации потоков есть поле Terminated. Поток должен время от времени проверять это поле на true и если это так, то должен завершать свою работу (в общем случае - просто подчистить свои ресурсы и вызвать exit).
 
 А тот, кому нужно, что бы поток завершился, просто вызывает
 имя_потока.Terminate;. Но никто не гарантирует, что поток сразу завершится (потоку туда ещё нужно дойти, проверить условие, завершиться). Если нужно дождаться завершения потока, то пишем так 
 имя_потока.Terminate;
имя_потока.WaitFor();Но только нужно понимать, что WaitFor - блокирующая операция и будет подтормаживать интерфейс (но если поток завершится быстро, то никаких проблем). 
|  | Ответ отправил: Вадим К (статус: Академик)Время отправки: 7 мая 2012, 11:06
 
 |  
 Мини-форум вопросаВсего сообщений: 3; последнее сообщение — 9 мая 2012, 09:30; участников в обсуждении: 2. 
|   | Shouldercannon (статус: Посетитель), 7 мая 2012, 14:04 [#1]:Предпочтительнее правильное завершение. 
 procedure TForm1.BStartClick(Sender: TObject);
begin
  ProgressBar1.Max := SLRepairFiles.Count;
 
  DownLoader_Thread := TDownLoader_Thread.Create(True);
  DownLoader_Thread.FSLRepairFiles := SLRepairFiles;
  DownLoader_Thread.FreeOnTerminate := True;
  DownLoader_Thread.Resume;
end;
 
procedure TDownLoader_Thread.Execute;
var
  HTTP: TIdHTTP;
  FS: TFileStream;
  i: Integer;
  Folder: string;
begin
  HTTP := TIdHTTP.Create(nil);
 
  CriticalSection.Enter;
 
  for i := 0 to FSLRepairFiles.Count - 1 do
  begin
    if Self.Terminated then Break; // Если главный поток приказал - умираем
 
    Folder := ExtractFilePath(Application.ExeName) + ExtractFilePath(FSLRepairFiles.Strings[i]);
    ForceDirectories(Folder);
    try
      try
        FS := TFileStream.Create(Folder + ExtractFileName(FSLRepairFiles.Strings[i]), fmCreate);
        HTTP.Get('http://77.108.194.247/' + FSLRepairFiles.Strings[i], FS);
      except
      end;
    finally
      FS.Free;
    end;
 
    Progress := i + 1;
 
    Synchronize(SyncProc);
  end;
 
  CriticalSection.Leave;
 
  HTTP.Free;
end;
 
procedure TDownLoader_Thread.SyncProc;
begin
  Form1.ProgressBar1.Position := Progress;
  if FSLRepairFiles.Count = Progress then Form1.Caption := 'Восстановлено: ' + IntToStr(FSLRepairFiles.Count) + '
файлов';
end;
 
procedure TForm1.BStopClick(Sender: TObject);
begin
  DownLoader_Thread.Terminate;
  DownLoader_Thread.WaitFor; // Ждём, когда DownLoader_Thread "мирно" помрёт
  DownLoader_Thread.Free;
end;Снова тажа ошибка. Что-то я упустил, но что? |  
|   | Вадим К (статус: Академик), 7 мая 2012, 19:00 [#2]:Выставлили DownLoader_Thread.FreeOnTerminate := True; ? тогда вызывать деструктор для треда уже не нужно. Потому что делфи сама его вызовет за вас. А так как есть вызов WaitFor, то к моменту вызова деструктора Вами, он уже будет вызван. 
 К тому же, есть одна тонкая специфика. Если задано FreeOnTerminate := True, то OnTerminate треда вызывается в контексте потока. А если false, то в контексте треда, который вызвал free.
 Галочка "подтверждения прочтения" - вселенское зло. |  
|   | Shouldercannon (статус: Посетитель), 9 мая 2012, 09:30 [#3]: procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Test_Thread.Terminate;
  Test_Thread.WaitFor;
  CriticalSection.Free;
end;
 
procedure TForm2.FormShow(Sender: TObject);
begin
  CriticalSection := TCriticalSection.Create;
 
  Test_Thread := TTest_Thread.Create(True);
  Test_Thread.FreeOnTerminate := False;
  Test_Thread.Resume;
end;
 
procedure TTest_Thread.Execute;
var
  i: Integer;
begin
  CriticalSection.Enter;
  for i := 0 to 100 do
  begin
    if Self.Terminated then Break;
    Sleep(100);
    s := IntToStr(i);
    Synchronize(SyncProc);
  end;
  CriticalSection.Leave;
end; |  11 мая 2012, 16:10: Статус вопроса изменён на решённый (изменил модератор DNK) Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте. |