Resuming suspended thread in Delphi 2010? Resuming suspended thread in Delphi 2010? multithreading multithreading

Resuming suspended thread in Delphi 2010?


The reason is that a Thread is not supposed to start itself.

The thread never knows when initialization is complete. Construction is not the same as initialization (construction should always be short and exception free; further initialization is done after construction).

A similar situation is a TDataSet: no TDataSet constructor should ever call Open, or set Active := True.

See also this blog entry by Wings of Wind.

You should either:

  • Create the TMyThread suspended by calling Create(true) and perform the Start outside your TMyThread class
  • Create the TMyThread non-suspeneded, making sure the Create constructor does full initialization, and let TThread.AfterConstruction start the thread.

Explanation of TThread usage:

Basically, a thread should be just that: the encapsulation of the context on which code is executed.

The actual code (the business logic) that is executed should then be in other classes.

By decoupling those two, you gain a lot of flexibility, especially initiating your business logic from within multiple places (which is very convenient when writing unit tests!).

This is the kind of framework you could use for that:

unit DecoupledThreadUnit;interfaceuses  Classes;type  TDecoupledThread = class(TThread)  strict protected    //1 called in the context of the thread    procedure DoExecute; virtual;    //1 Called in the context of the creating thread (before context of the new thread actualy lives)    procedure DoSetUp; virtual;    //1 called in the context of the thread right after OnTerminate, but before the thread actually dies    procedure DoTearDown; virtual;  protected    procedure DoTerminate; override;    procedure Execute; override;  public    constructor Create;    procedure AfterConstruction; override;  end;implementationconstructor TDecoupledThread.Create;begin  // create suspended, so that AfterConstruction can call DoSetup();  inherited Create(True);end;procedure TDecoupledThread.AfterConstruction;begin  // DoSetUp() needs to be called without the new thread in suspended state  DoSetUp();  // this will unsuspend the underlying thread  inherited AfterConstruction;end;procedure TDecoupledThread.DoExecute;beginend;procedure TDecoupledThread.DoSetUp;beginend;procedure TDecoupledThread.DoTearDown;beginend;procedure TDecoupledThread.DoTerminate;begin  inherited DoTerminate();  // call DoTearDown on in the thread context right before it dies:  DoTearDown();end;procedure TDecoupledThread.Execute;begin  // call DoExecute on in the thread context  DoExecute();end;end.

You could even make it event based by something like this:

unit EventedThreadUnit;interfaceuses  Classes,  DecoupledThreadUnit;type  TCustomEventedThread = class(TDecoupledThread)  private    FOnExecute: TNotifyEvent;    FOnSetUp: TNotifyEvent;    FOnTearDown: TNotifyEvent;  strict protected    procedure DoExecute; override;    procedure DoSetUp; override;    procedure DoTearDown; override;  public    property OnExecute: TNotifyEvent read FOnExecute write FOnExecute;    property OnSetUp: TNotifyEvent read FOnSetUp write FOnSetUp;    property OnTearDown: TNotifyEvent read FOnTearDown write FOnTearDown;  end;  // in case you want to use RTTI  TEventedThread = class(TCustomEventedThread)  published    property OnExecute;    property OnSetUp;    property OnTearDown;  end;implementation{ TCustomEventedThread }procedure TCustomEventedThread.DoExecute;var  TheOnExecute: TNotifyEvent;begin  inherited;  TheOnExecute := OnExecute;  if Assigned(TheOnExecute) then    TheOnExecute(Self);end;procedure TCustomEventedThread.DoSetUp;var  TheOnSetUp: TNotifyEvent;begin  inherited;  TheOnSetUp := OnSetUp;  if Assigned(TheOnSetUp) then    TheOnSetUp(Self);end;procedure TCustomEventedThread.DoTearDown;var  TheOnTearDown: TNotifyEvent;begin  inherited;  TheOnTearDown := OnTearDown;  if Assigned(TheOnTearDown) then    TheOnTearDown(Self);end;end.

Or adapt it for DUnit TTestCase descendants like this:

unit TestCaseThreadUnit;interfaceuses  DecoupledThreadUnit,  TestFramework;type  TTestCaseRanEvent = procedure (Sender: TObject; const TestResult: TTestResult) of object;  TTestCaseThread = class(TDecoupledThread)  strict private    FTestCase: TTestCase;  strict protected    procedure DoTestCaseRan(const TestResult: TTestResult); virtual;    function GetTestCase: TTestCase; virtual;    procedure SetTestCase(const Value: TTestCase); virtual;  protected    procedure DoExecute; override;    procedure DoSetUp; override;    procedure DoTearDown; override;  public    constructor Create(const TestCase: TTestCase);    property TestCase: TTestCase read GetTestCase write SetTestCase;  end;implementationconstructor TTestCaseThread.Create(const TestCase: TTestCase);begin  inherited Create();  Self.TestCase := TestCase;end;procedure TTestCaseThread.DoExecute;var  TestResult: TTestResult;begin  if Assigned(TestCase) then  begin    // this will call SetUp and TearDown on the TestCase    TestResult := TestCase.Run();    try      DoTestCaseRan(TestResult);    finally      TestResult.Free;    end;  end  else    inherited DoExecute();end;procedure TTestCaseThread.DoTestCaseRan(const TestResult: TTestResult);beginend;function TTestCaseThread.GetTestCase: TTestCase;begin  Result := FTestCase;end;procedure TTestCaseThread.SetTestCase(const Value: TTestCase);begin  FTestCase := Value;end;procedure TTestCaseThread.DoSetUp;begin  if not Assigned(TestCase) then    inherited DoSetUp();end;procedure TTestCaseThread.DoTearDown;begin  if not Assigned(TestCase) then    inherited DoTearDown();end;end.

--jeroen


Short answer: call inherited Create(false) and omitt the Start!

The actual Start of a non-create-suspended thread is done in AfterConstruction, which is called after all constructors have been called.