Приостановка потока со свойством

У меня есть объект TThread, и я хочу иметь возможность запускать/останавливать поток с помощью кнопки в главной форме программы. Я искал способы сделать это, и до сих пор у меня есть следующие идеи:

  1. Завершите и освободите поток, когда пользователь нажмет кнопку «Стоп», и создайте новый, когда он нажмет кнопку «Пуск».
  2. Используйте сон, чтобы задержать поток (я не хочу этого делать)
  3. Иметь свойство, которое является логическим значением, чтобы определить, приостановлен ли поток или нет. Код в Execute будет выполняться только в том случае, если это логическое значение равно false.

Я склоняюсь к №3. Будет ли установка логического свойства для объекта TThread из основной формы потокобезопасным?

Какой из этих вариантов или какая-либо лучшая альтернатива мне следует выбрать? Я впервые использую темы, поэтому любая помощь приветствуется.


person user1970794    schedule 21.07.2016    source источник
comment
Последние версии Delphi не позволяют приостанавливать/возобновлять потоки, потому что весь дизайн (и концепция) были ошибочными. Вам следует избегать попыток сделать это и вместо этого переключиться на использование сигнальных событий (например, TEvent). См. различные типы событий в SyncObjs.   -  person Ken White    schedule 22.07.2016
comment
см. также stackoverflow.com/questions/4401171/   -  person kludg    schedule 22.07.2016


Ответы (2)


1. Завершите и освободите поток, когда пользователь нажмет «Стоп», и создайте новый, когда он нажмет «Пуск».

Это конечно вариант, если накладные расходы минимальны.

3. Иметь свойство, которое является логическим значением, чтобы определить, приостановлен ли поток или нет. Код в Execute будет выполняться только в том случае, если это логическое значение равно false.

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

Будет ли установка логического свойства для объекта TThread из основной формы потокобезопасным?

Это так же потокобезопасно, как вызов TThread.Terminate(), который просто устанавливает логическое свойство TThread.Terminated.

Какой из этих вариантов или какая-либо лучшая альтернатива мне следует выбрать?

Я использую вариант № 4 - использование сигнальных событий вместо логических. Например:

type
  TMyThread = class(TThread)
  private
    FRunEvent, FTermEvent: TEvent;
    FWaitEvents: THandleObjectArray;
    procedure CheckPause;
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure Pause;
    procedure Unpause;
  end;

constructor TMyThread.Create;
begin
  inherited Create(False);

  FRunEvent := TEvent.Create(nil, True, True, '');
  FTermEvent := TEvent.Create(nil, True, False, '');

  SetLength(FWaitEvents, 2);
  FWaitEvents[0] := FRunEvent;
  FWaitEvents[1] := FTermEvent;
end;

destructor TMyThread.Destroy;
begin
  FRunEvent.Free;
  FTermEvent.Free;
  inherited;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    // do some work...
    CheckPause;
    // do some more work...
    CheckPause;
    // do some more work...
    CheckPause;
    //...
  end;
end;

procedure TMyThread.TerminatedSet;
begin
  FTermEvent.SetEvent;
end;

procedure TMyThread.CheckPause;
var
  SignaledEvent: THandleObject;
begin
  while not Terminated do
  begin
    case TEvent.WaitForMultiple(FWaitEvents, INFINITE, False, SignaledEvent) of
      wrSignaled: begin
        if SignaledEvent = FRunEvent then Exit;
        Break;
      end;
      wrIOCompletion: begin
        // retry
      end;
      wrError: begin
        RaiseLastOSError;
    end;
  end;
  SysUtils.Abort;
end;

procedure TMyThread.Pause;
begin
  FRunEvent.ResetEvent;
end;

procedure TMyThread.Unpause;
begin
  FRunEvent.SetEvent;
end;
person Remy Lebeau    schedule 22.07.2016
comment
Реми, для чего используется SysUtils.Abort; в методе CheckPause. - person Nasreddine Galfout; 21.11.2017
comment
Он создает исключение EAbort для немедленного завершения потока (поскольку Execute() не перехватывает его) при следующем вызове CheckPause() после вызова Terminate(). - person Remy Lebeau; 21.11.2017

Ознакомьтесь с вики для Delphi по запуску и остановке потоков здесь: http://docwiki.embarcadero.com/RADStudio/Berlin/en/Starting_and_Stopping_Threads

Это применимо еще в Delphi 7. Это может быть применимо и раньше, но я не могу подтвердить более ранние версии.

person David Pratt    schedule 22.07.2016
comment
Это комментарий к вопросу, а не ответ. Ответы содержат соответствующую информацию здесь, в самом сообщении, со ссылками, используемыми только для предоставления дополнительной справки. Ответы должны стоять сами по себе, чтобы они содержали ценность, если удаленное местоположение по какой-либо причине недоступно (перемещено, отключено от сети или что-то еще). Ссылку только на ответы не делайте. - person Ken White; 22.07.2016
comment
Добро пожаловать в Stack Overflow. Ты не ответил на вопрос. Вопрос требует рекомендации, но вы забыли ее предоставить. Пожалуйста, отредактируйте свой ответ, чтобы он отвечал тому, что задает вопрос. - person Rob Kennedy; 22.07.2016