TThread.CreateAnonymousthread/FreeOnTerminate, но затем с TTask/ITask

Фон

Используя TThread.CreateANonymousThread(aProc:TProc), я могу создать поток, который уничтожает объект потока после завершения потока. (или, альтернативно, установив FreeOnTerminate в true для объекта thread). Это позволяет процедуре инициатора потока завершиться и выйти за пределы области действия, в то время как поток продолжает работать. (Это то, что я ищу)

procedure StartProcess
begin

  var lTask:=TThread.CreateAnonymousThread(
    procedure
    begin
       ... Do lengthy thread stuff here 
    end
    );
  ...
  lTask.Start;
end;

Проблема возникает из-за того, что TTask.Create возвращает интерфейс ITask, который освобождается, когда код инициатора потока сбрасывает свой контекст (с RefCount переходит на 0 -> вызывается Destroy), в результате чего поток генерирует AV.

procedure StartProcess
begin

  var lTask:=TTask.Create(
    procedure
    begin
       ... Do lengthy thread stuff here 
    end
    );
  ...
  lTask.Start;
end; /// past this point, the subthread wil crash because the underlying task object is destroyed

В случае OmniThread у нас есть решение под названием IOmniTaskCOntrol.Unobserved, которое позволяет избежать уничтожения объекта задачи до его завершения.

Почему?

РЕДАКТИРОВАТЬ: мне нравится интерфейс ITask по сравнению с классом TThread, потому что он допускает слабую связь и внедрение кода. (предыдущая: Поскольку TThread может быть устаревшим: просто забудьте об этом)

Вопрос

Мне было интересно, можно ли (и как!) с помощью TTask.Create(aProc:TProc) и интерфейса ITask добиться того же самого. Анализ исходного кода мне пока не помог.


person H.Hasenack    schedule 01.03.2021    source источник
comment
Нет, TThread не устаревает.   -  person Andreas Rejbrand    schedule 01.03.2021
comment
Этот вопрос требует минимального воспроизводимого примера, включая используемую вами версию Delphi, но я знаю это только потому, что знаю есть проблемы с захватом встроенных переменных в 10.4 и 10.4.1. Но из кода в вашем вопросе вы ничего не фиксируете, поэтому действительно неясно, в чем именно заключается ваша проблема.   -  person Dalija Prasnikar    schedule 01.03.2021
comment
TThread не станет устаревшим, потому что TTask фактически использует TThread.   -  person Dalija Prasnikar    schedule 01.03.2021
comment
Мне нравится интерфейс ITask (принцип слабой связи/внедрения кода), поэтому я предпочитаю решение ITask решению TThread. Просто забудьте замечание о том, что TThread устарел, это не имеет значения.   -  person H.Hasenack    schedule 01.03.2021


Ответы (1)


Ответ прост: ничего особенного делать не нужно. Интерфейс ITask, возвращаемый вызовом TTask.Create, также удерживается внутренне методом InternalExecute, поэтому нижележащий объект TTask будет уничтожен посредством подсчета ссылок. Если основной поток не удерживает интерфейс ITask, подпоток будет. Пока не прекратилось.

Так что использовать TTask таким образом довольно просто.

ПРИМЕЧАНИЕ: в RS10.4.2 это работает, я подозреваю, что использование захваченных переменных интерфейса может вызвать проблему в 10.4.1 и более ранних версиях из-за проблем со встроенными переменными в сочетании с анонимными процессами. (Не пробовал)

person H.Hasenack    schedule 01.03.2021
comment
Просто избавьтесь от встроенной переменной и все. Если вам по какой-то причине нужна переменная, переместите ее в блок var процедуры. - person Dalija Prasnikar; 01.03.2021
comment
Да, кажется, у меня что-то еще не так. Кажется, я не могу каким-то образом воссоздать свой AV, даже если я удалю оператор lTask:=nil. Я явно должен провести повторное расследование. Я также обнаружил TTask.CurrentTask, который на самом деле содержит ITask для текущего потока. Прохладный. - person H.Hasenack; 01.03.2021