Как остановить поток в Delphi?

Вопрос или проблема

У меня есть button1 и label1. Когда button1 нажимают, label1 начинает самостоятельно считать в своем потоке.

Проблема в том, что мне не удалось остановить поток, когда нажимают button2.

Вот код, с которым я работаю:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, System.SyncObjs;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private

  public

  end;

{ TMyThread — это класс, который будет обрабатывать работу в фоновом режиме для Label1 }
  MyThread1 = class(TThread)
  private
    count: Integer;
    msg: string;
    stopThread:boolean;
  protected
    procedure Execute; override;
    procedure UpdateUI;
  public
    constructor Create;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

{ Конструктор MyThread }
constructor MyThread1.Create;
begin
  inherited Create(False); // False означает, что он начнет сразу
  FreeOnTerminate := True; // Освободить память по завершении
end;

{ Рабочий поток для Label1 }
procedure MyThread1.Execute;
var
  i: Integer;
begin
  for i := 1 to 1000 do
  begin
    // Проверьте, был ли поток завершен. Если да, то выходите из цикла.
    if Terminated then
      Exit;

    Sleep(10); // Симуляция работы (1 секунда за итерацию)
    msg := '# ' + IntToStr(i);
    Synchronize(UpdateUI); // Безопасно обновить UI из главного потока
  end;

  Synchronize(UpdateUI);
end;

{ Обновить UI для Label1 }
procedure MyThread1.UpdateUI;
begin
  Inc(count);
  Form1.Label1.Text := msg;

  // Когда закончено:
  if (count = 1000) then
  begin
    ShowMessage('Поток 1 завершен!');
  end;
end;

{ Запустите поток, когда нажимают button1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
  MyThread1.Create; // Создайте и запустите первый поток для Label1
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  // Процедура для остановки потока MyThread1 здесь.
end;

end.

Я пытался остановить поток, и он не остановился.

Что именно может быть проблемой, из-за которой я не могу завершить этот поток?

Вы не используете член stopThread, поэтому избавьтесь от него. Код вашего потока Execute() смотрит на свойство Terminated потока, поэтому вызовите метод Terminate() потока.

Однако ваш button1 не сохраняет объект потока, который он создает, поэтому button2 не может получить к нему доступ.

Попробуйте что-то более похожее на это:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, System.SyncObjs;

type
  MyThread1 = class;

  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FMyThread: MyThread1;
    procedure ThreadTerminated(Sender: TObject);
  public
  end;

{ TMyThread — это класс, который будет обрабатывать работу в фоновом режиме для Label1 }
  MyThread1 = class(TThread)
  private
    count: Integer;
    msg: string;
  protected
    procedure Execute; override;
    procedure UpdateUI;
  public
    constructor Create;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

{ Конструктор MyThread }
constructor MyThread1.Create;
begin
  inherited Create(True); // True означает, что он не начнет сразу
  FreeOnTerminate := True; // Освободить память по завершении
end;

{ Рабочий поток для Label1 }
procedure MyThread1.Execute;
var
  i: Integer;
begin
  for i := 1 to 1000 do
  begin
    // Проверьте, был ли поток завершен. Если да, то выходите из цикла.
    if Terminated then
      Break;

    Sleep(10); // Симуляция работы (1 секунда за итерацию)
    msg := '# ' + IntToStr(i);
    Synchronize(UpdateUI); // Безопасно обновить UI из главного потока
  end;

  Synchronize(UpdateUI);
end;

{ Обновить UI для Label1 }
procedure MyThread1.UpdateUI;
begin
  Inc(count);
  Form1.Label1.Text := msg;
end;

{ Запустите поток, когда нажимают button1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
  if FMyThread <> nil then Exit;
  FMyThread := MyThread1.Create; // Создайте и запустите первый поток для Label1
  FMyThread.OnTerminated := ThreadTerminated;
  FMyThread.Start;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  // Процедура для остановки потока MyThread1 здесь.
  if FMyThread <> nil then
    FMyThread.Terminate;
end;

procedure TForm1.ThreadTerminated(Sender: TObject);
begin
  // Когда закончено:
  FMyThread := nil; 
  ShowMessage('Поток 1 завершен!');
end;

end.

.

Ответ или решение

Для того чтобы остановить поток в Delphi, вам следует рассмотреть несколько ключевых моментов, касающихся управления потоками и их завершения. Ваша текущая модель потока имеет несколько недочетов, которые не позволяют корректно остановить его выполнение. Давайте разберем, как это можно сделать.

1. Проблема с сохранением ссылки на поток

В первой версии вашего кода, вы создаете поток в обработчике события Button1Click, но не сохраняете ссылку на него в переменной. Это значит, что в обработчике Button2Click, у вас нет доступа к этому потоку, и вы не можете вызвать метод для его завершения.

Следует сохранить созданный поток в приватной переменной класса формы, чтобы иметь возможность манипулировать им после его создания.

2. Использование механизма завершения потоков

Чтобы корректно завершить поток, используйте свойство Terminated. Оно позволяет безопасно сигнализировать о завершении работы потока, проверяя это состояние из метода Execute.

3. Реализация кода

Пример улучшенного кода, который корректно справляется с задачей завершения потока, выглядит следующим образом:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FMyThread: TThread; // Храним ссылку на поток
  public
  end;

{ Определение потока }
type
  TMyThread = class(TThread)
  private
    FCount: Integer;
    FMsg: string;
    procedure UpdateUI;
  protected
    procedure Execute; override;
  public
    constructor Create;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

constructor TMyThread.Create;
begin
  inherited Create(True); // Не запускаем поток сразу
  FreeOnTerminate := True; // Освобождаем память после завершения
end;

procedure TMyThread.Execute;
var
  i: Integer;
begin
  for i := 1 to 1000 do
  begin
    if Terminated then
      Exit;

    Sleep(10); // Эмулируем работу
    FMsg := '# ' + IntToStr(i);
    Synchronize(UpdateUI); // Обновляем UI
  end;
end;

procedure TMyThread.UpdateUI;
begin
  Form1.Label1.Text := FMsg; // Обновляем метку
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(FMyThread) then Exit; // Если поток уже существует, выходим
  FMyThread := TMyThread.Create; // Создаем поток
  FMyThread.Start; // Запускаем поток
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if Assigned(FMyThread) then
  begin
    FMyThread.Terminate; // Сигнализируем о завершении
    FMyThread.WaitFor; // Ожидаем завершения потока
    FMyThread := nil; // Обнуляем ссылку после завершения
    ShowMessage('Поток завершён!'); // Информируем пользователя
  end;
end;

end.

Объяснение изменений:

  1. Хранение потока: Поток теперь хранится в переменной FMyThread, что позволяет обращаться к нему в обоих обработчиках.
  2. Метод Terminate: Мы вызываем Terminate, чтобы сигнализировать о завершении работы потока, а затем ждем его завершения с помощью WaitFor.
  3. Проверка Assigned: Это позволяет избежать изменения состояния уже завершенного потока.

Заключение

С помощью предложенных изменений вы сможете корректно управлять жизненным циклом потока в приложении Delphi. Этот подход не только улучшает стабильность вашего кода, но и варьирует пользовательский опыт, позволяя пользователю завершить фоновые операции по своему усмотрению.

Оцените материал
Добавить комментарий

Капча загружается...