Правильный способ уничтожения объекта TThread

Этот вопрос может показаться тривиальным, но я надеюсь, вы не проигнорируете его.
Перед уничтожением объекта TThread обычно необходимо дождаться завершения потока, вызвавшего метод TThread.Execute(), поскольку только тогда мы можем быть уверены, что, например, к объектам, уничтоженным внутри деструктора класса, больше нет доступа. Поэтому необходимо вызвать Terminate, чтобы установить флаг Termination, который поток должен проверить, чтобы узнать, выходить или нет, а затем вызвать метод WaitFor().

Поскольку поток может быть приостановлен, я думаю, что было бы хорошо возобновить его перед вызовом WaitFor, так как в противном случае вызывающий поток был бы заблокирован. И поскольку поток может быть приостановлен несколько раз, он должен быть возобновлен столько же раз, верно?

while Suspended do
  Resume;

Если поток был создан приостановленным, нам не нужно беспокоиться о том, что метод TThread.Execute() будет вызываться, когда мы возобновляем поток только для его прекращения - не будет (пожалуйста, исправьте меня, если я ошибаюсь).

То, что я изложил, предполагает использование следующих строк кода для каждого освобождаемого объекта TThread:

MyThread.Terminate;
while MyThread.Suspended do
  MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;

К сожалению, когда мы уничтожаем наше приложение, которое создало несколько потоков, написание такого фрагмента кода для каждого уничтожаемого объекта TThread излишне делает код очень длинным и, возможно, даже непрозрачным.

Поэтому я пришел к выводу, что все они могут быть помещены в переопределенный деструктор класса TThread, благодаря которому было бы достаточно вызвать MyThread.Free (или MyThread.Terminate, если установлен MyThread.FreeOnTerminate), не заботясь о том, будет ли уничтожен объект является объектом TThread или нет:

destructor TMyThread.Destroy;
begin
  //if FreeOnTerminate, the calling thread cannot wait for itself
  if GetCurrentThreadId <> ThreadId then
  begin
    Terminate;
    while Suspended do
      Resume;
    WaitFor;
  end;

  {free all objects created in this class}

  inherited Destroy;
end;

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

Спасибо за ваши предложения заранее.

2 ответа

Решение

Многое из того, что вы предлагаете, уже выполнено в деструкторе TThread.Destroy, и вызов TMyThread.free сделает именно то, что вы предлагаете. Чтобы очистить любые объекты, принадлежащие классу потока, вы можете выполнить это в событии OnTerminate, которое будет вызываться как часть логики завершения потока.

Не существует универсального способа остановить поток, так же как нет универсального способа (изящно) остановить процесс. Каждый отличается.

Для некоторых потоков достаточно установить его Terminated собственность через Terminate метод. Другие потоки, однако, вызывают такие функции, как GetMessage или же MsgWaitForMultipleObjects, который будет блокироваться до тех пор, пока что-то не произойдет, например, о прибытии сообщения или получении указателя ядра TThread.Terminate не может заставить ни одну из этих вещей произойти, поэтому она не может заставить эти потоки перестать работать. Когда я писал такие потоки, я предоставлял свои собственные функции для уведомления их о прекращении работы. Я мог бы позвонить PostThreadMessage заставить сообщение в очередь потока, или я мог бы сигнализировать событие, которое класс потока предоставил для уведомления его о запросе на прекращение.

Не беспокойтесь о возобновлении приостановленного потока. Вы действительно не должны приостанавливать их в любом случае. Единственный безопасный способ приостановить поток - это приостановить сам поток, и, как только вы это сделаете, вы гарантированно получите, по крайней мере, два потока, контролирующих время выполнения потока: сам поток, чтобы приостановить его, и, по крайней мере, еще один поток, чтобы возобновить это снова. Поток должен контролировать собственное выполнение.

Было бы здорово, если TThread.Terminate были виртуальными. Тогда каждый класс потока может предоставить собственный способ уведомления о прекращении работы. Некоторые могут просто установить Terminatedи другие могут публиковать сообщения, сигнализировать о событиях или делать все, что им нужно. Тем не менее, не виртуальный метод плохо работает с потоками, которые проводят много времени в ожидании других вещей. Текущий способ работает только для потоков, которые могут часто опрашивать их Terminated свойства.

Некоторые темы имеют свои FreeOnTerminate свойства установлены. Для этих потоков ваш код небезопасен. Технически, не безопасно вызывать какие-либо методы для таких объектов, так как поток может завершиться в любое время. Но даже если вы знаете, что поток все еще работает и объект потока все еще существует, объект определенно прекратит существовать через некоторое время после вызова Terminate, Ты не можешь позвонить WaitFor на свободном в конце объекте потока, и вы определенно не можете вызвать Free,

Другие вопросы по тегам