Исполняющий поток TThread.OnTerminate

В справке Delphi для TThread.OnTerminate указано, что:

Метод, назначенный событию OnTerminate, выполняется в контексте основного потока, а не в контексте завершаемого потока.

Это даже тот случай, когда поток создается в другом потоке, чем основной поток?

Итак, вызывается ли OnTerminate в потоке, который создал TThread, или он вызывается в основном потоке? IDE не говорит мне об этом. Когда я отлаживаю, я не вижу активного потока в событии OnTerminate. :-/


person Wolfgang Bures    schedule 20.01.2021    source источник
comment
На самом деле можно увидеть идентификатор потока в IDE при отладке. Панели Call Stack, Список наблюдения и Локальные переменные отображают идентификатор текущего потока. Я только что попытался добавить точку останова в обработчик OnTerminate потока и вижу, что его идентификатор потока равен 12888. И если я поставлю точку останова в какой-либо код графического интерфейса (например, обработчик OnClick кнопки, обработчик OnCreate основной формы, обработчик пункта меню и т. д. .) Я вижу, что основной поток графического интерфейса - 12888. Отсюда мой вывод... Ну, я думаю, вы догадываетесь! :)   -  person Andreas Rejbrand    schedule 20.01.2021


Ответы (1)


Документация правильная. Обработчик событий OnTerminate по умолчанию всегда запускается в основном потоке. Внутри TThread.DoTerminate() (который вызывается после выхода из метода Execute() потока) использует TThread.Synchronize() для вызова обработчика:

function ThreadProc(Thread: TThread): Integer;
var
  ...
begin
  ...
  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      ...
    end;
  finally
    ...
    Thread.DoTerminate;
    ...
  end;
end;

procedure TThread.DoTerminate;
begin
  if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;

procedure TThread.CallOnTerminate;
begin
  if Assigned(FOnTerminate) then FOnTerminate(Self);
end;

Если вы хотите, чтобы обработчик OnTerminate запускался в контексте завершающего потока (или любого другого потока, который вы хотите), вы можете просто переопределить DoTerminate() для вызова обработчика, как вы хотите, например:

type
  TMyThread = class(TThread)
    ...
  protected
    ...
    procedure Execute; override;
    procedure DoTerminate; override;
    ...
  end;

procedure TMyThread.Execute;
begin
  ...
end;

procedure TMyThread.DoTerminate;
begin
  // do whatever you want here, but DON'T call inherited!
  if Assigned(OnTerminate) then OnTerminate(Self);
end;
person Remy Lebeau    schedule 20.01.2021