#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
конструктор для инициализации этих членов. Но этот вызов не обязательно должен быть первым оператором в производном конструкторе нежелательного, хотя это наиболее распространенное использование. Я обновил свой ответ.