Beim Debuggen des Fehlers "Cannot start a task that has already completed" bzw. "Eine bereits abgeschlossene Aufgabe kann nicht gestartet werden" ist mir aufgefallen, dass die korrekte Nutzung von TThread.CreateAnonymousThread und TTask.Run missverständlich sein kann. Die Benennung ist zwar schon korrekt, aber ich verstehe, weshalb jemand, der TThread.CreateAnonymousThread kennt, dies bei TTask.Run falsch macht.

Was ist passiert? Der Code sah so aus:

  TTask.Run(procedure
    begin
      // etwas Code
    end).Start;

Wenn man folgendes Pattern kennt, weiß man, weshalb derjenige das so geschrieben hat:

  TThread.CreateAnonymousThread(procedure
    begin
      // etwas Code
    end).Start;

Das Problem ist nur, dass TTask.Run so aussieht:

class function TTask.Run(const Func: TProc): ITask;
begin
  Result := TTask.Create(Func, TThreadPool.Default);
  Result.Start;
end;

Heißt:
Die Aufgabe wird direkt gestartet. Meistens dauert die Aufgabe lange genug, dass das eigene Start ausgeführt wird, bevor die Aufgabe beendet ist. Wenn dort aber eine Abbruchbedingung drin ist, kann es passieren, dass die Aufgabe da schon beendet ist. Dann kommt obige Fehlermeldung.

Ja, Run und Create... sind eindeutig benannt. Ich musste trotzdem mehrfach hinschauen, bis mir das aufgefallen ist.