Могу ли я выполнить некоторые инициализации перед «Унаследованным созданием»?

#multithreading #delphi #delphi-10.3-rio

#многопоточность #delphi #delphi-10.3-rio

Вопрос:

Я хочу создать и запустить поток, все с помощью одной командной строки TClientCopyThread.Create(...) . Для этого я должен создать поток с Suspended = False помощью, чтобы он мог выполняться немедленно. Я знаю, что когда я пишу constructor новый объект, прежде всего я должен вызвать inherited Create so, чтобы экземпляр объекта был создан, а затем выполнить мои инициализации. Но здесь, если я вызову inherited поток, он запустится без инициализированных параметров. Я пытаюсь вызвать inherited last, и кажется, что это работает (я не получаю никаких нарушений доступа), но я не знаю наверняка, совпадение это или нет.

   TClientCopyThread = class(TThread)
  private
    OwnGUID: String;
    SrcPath, DestPath: String;
    Files: TFileNames;
    RemoveIt: Boolean;
  protected
    procedure Execute; override;
  public
    constructor Create(const GUID, ASrcPath, ADestPath: String;
     const FileNames: TFileNames; RemoveSrc: Boolean);
  end;

constructor TClientCopyThread.Create(const GUID, ASrcPath, ADestPath: String;
 const FileNames: TFileNames; RemoveSrc: Boolean);
var I: Integer;
begin
 SrcPath:=  Copy(ASrcPath, 1, Length(ASrcPath));
 DestPath:= Copy(ADestPath, 1, Length(ADestPath));
 SetLength(Files, Length(FileNames));
 for I:= 0 to High(Files) do
  Files[I]:= Copy(FileNames[I], 1, Length(FileNames[I]));
 RemoveIt:= RemoveSrc;
 FreeOnTerminate:= True;
 inherited Create;
end;
  

Комментарии:

1. Хорошим местом для запуска потока является AfterConstruction, где все конструкторы были выполнены, и экземпляр должен быть стабильным.

2. @J… Я тоже думал об этом, но я получаю сообщение об ошибке, если я пытаюсь перейти к Start потоку из конструктора. / Хорошо, значит, память для моего нового объекта уже выделена, когда я ввожу в конструктор?

3. @UweRaabe TThread уже делает именно это, когда CreateSuspended=False

4. @MarusNebunu TThread.Start() вызывает EThread исключение, если TThread объект был создан с CreateSuspended=False помощью, или если поток уже завершен, или если Start() он уже был вызван, или если TThread указатель был получен при вызове TThread.CurrentThread в не- TThread потоке. Итак, при использовании должно быть совершенно безопасно вызывать Start() внутри производного конструктора inherited Create(True) . Если это не работает, то это ошибка, которую необходимо исправить.

Ответ №1:

Чтобы ответить на ваш конкретный вопрос — ДА, вы можете вызвать inherited Create в любой момент во время производного конструктора. Это НЕ обязательно должен быть первый оператор (такой же, inherited Destroy как и последний оператор в деструкторе). Память для объекта класса уже полностью выделена до вызова любых конструкторов, поэтому безопасно инициализировать члены вашего производного класса перед вызовом inherited конструктора. Однако при обращении к членам базового класса из производного конструктора вы должны inherited сначала вызвать конструктор, чтобы инициализировать их, прежде чем обращаться к ним.

При этом ваше понимание того, как TThread работает конструктор, просто неверно. Начиная с Delphi 6 и далее, конструктор базового класса TThread всегда создает базовый поток ОС в приостановленном режиме, а затем возобновляет поток TThread.AfterConstruction() после того, как все конструкторы полностью завершили работу, если TThread объект создан с CreateSuspended=False помощью . Итак, ваше утверждение о том, что вызов inherited Create with CreateSuspended=False немедленно запустит поток, НЕ соответствует действительности с 2001 года, когда был выпущен Delphi 6. Базовый поток ОС НЕ начнет выполняться до тех пор, пока ваш TClientCopyThread.Create() конструктор не завершит работу. Таким образом, ваш Execute() метод никогда не будет действовать на неинициализированных членах, независимо от того, как вы установили CreateSuspended .

То, что вы описываете (поток, запущенный до инициализации членов), было ошибкой в Delphi 5 и более ранних версиях, которая была исправлена в Delphi 6.

Комментарии:

1. Это справедливо только в том случае, если родительский класс не создает никаких внутренних / вложенных объектов / классов в своем собственном конструкторе. Если он вызывается недостаточно inherited Create быстро, это будет означать, что вы можете попытаться получить доступ к этим объектам из конструктора производного класса до их правильного создания, что затем приведет к нарушению доступа. Так что да, хотя это справедливо для класса TThread, поскольку у него нет никаких внутренних объектов, многие из них не верны для других классов.

2. @SilverWarior очевидно, что если конструктор производного класса хочет получить доступ к членам базового класса, ему необходимо сначала вызвать inherited конструктор для инициализации этих членов. Но этот вызов не обязательно должен быть первым оператором в производном конструкторе нежелательного, хотя это наиболее распространенное использование. Я обновил свой ответ.