2

I want to create and start a thread, all with one command line TClientCopyThread.Create(...). For this, I must create the thread with Suspended = False, so that it can run immediately. I know that when I write a constructor of a new object, first of all I must call the inherited Create so that the instance of the object is created, and then do my initializations. But here, if I call inherited the thread will start without initialized parameters. I try to call inherited last and it seems it's working (I don't receive any access violation), but I don't know for sure if this is a coincidence or not.

  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;
Marus Gradinaru
  • 2,824
  • 1
  • 26
  • 55
  • A good place to start the thread is in AfterConstruction where all constructors have been executed and the instance should be stable. – Uwe Raabe Sep 18 '20 at 21:05
  • @J... I thought about that too, but I get an error message if I try to `Start` the thread from constructor. / Ok, so the memory for my new object is already allocated when I enter in constructor ? – Marus Gradinaru Sep 18 '20 at 21:15
  • 1
    @UweRaabe `TThread` already does *exactly* that when `CreateSuspended=False` – Remy Lebeau Sep 18 '20 at 21:26
  • 1
    @MarusNebunu `TThread.Start()` raises an `EThread` exception if the `TThread` object was created with `CreateSuspended=False`, or if the thread has already finished running, or if `Start()` has already been called, or if the `TThread` pointer was obtained from calling `TThread.CurrentThread` in a non-`TThread` thread. So, it should be perfectly safe to call `Start()` inside a derived constructor when using `inherited Create(True)`. If that is not working, then that is a bug that would need to be fixed. – Remy Lebeau Sep 18 '20 at 21:36

1 Answers1

10

To answer your specific question - YES, you can call inherited Create at any point during a derived constructor. It DOES NOT need to be the first statement (same with inherited Destroy as the last statement in the destructor). Memory for the class object has already been allocated in full before any constructors are called, so it is safe to initialize members of your derived class before calling the inherited constructor. However, when accessing base class members from the derived constructor, you should call the inherited constructor first to initialize them before accessing them.

That being said, your understanding of how the TThread constructor works is just plain wrong. Since Delphi 6 onwards, the base class TThread constructor always creates the underlying OS thread in suspended mode, and then resumes the thread in TThread.AfterConstruction() after all constructors have fully exited, if the TThread object is constructed with CreateSuspended=False. So, your claim that calling inherited Create with CreateSuspended=False will start the thread running immediately has NOT been true since 2001 when Delphi 6 was released. The underlying OS thread will NOT start running until after your TClientCopyThread.Create() constructor has exited. Thus, your Execute() method will never act on uninitialized members, regardless of how you set CreateSuspended.

What you describe (the thread running before members were initialized) was a bug in Delphi 5 and earlier, which was fixed in Delphi 6.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • This holds true only if parent class isn't creating any internal/nested objects/classes in its own constructor. If it is then not calling `inherited Create` fast enough would mean that you might try to access these objects from derived class constructor before they were properly created which would then cause Access Violation. So yeah while this do holds true with TThread class since it does not have any internal objects it many not hold true with other classes. – SilverWarior Sep 21 '20 at 13:34
  • @SilverWarior obviously, if a derived class constructor wants to access base class members, it needs to call the `inherited` constructor first to initialize those members. But that call does not *need* to be the *first* statement in the derived constructor of not desired, though that is the most common usage. I've updated my answer. – Remy Lebeau Sep 21 '20 at 15:38