Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threaded initialization and cleanup in delphi

I was reading the answer to another question here about the need to run coinitialize and couninitialize to connect to ADO objects. I need to do something similar to perform soap calls in a thread.

Is there a way to override a TThread object to have something automatically run before and after execute inside the thread?

For this example, we're converting to a SOAP backend and have to do a ton of that and it would save a bit of time to just override a new SOAP friendly TThread than to add coinitialize and couninitialze to every thread. But in general, initializing and cleaning up the thread inside the thread sometimes seems like a good idea. Right now it seems like you can only do one or the other.

like image 556
Peter Turner Avatar asked Oct 04 '13 12:10

Peter Turner


Video Answer


2 Answers

Perhaps you want something like this:

type
  TMyThread = class sealed(TThread)
  private
    FOnBeforeExecute: TProc;
    FOnExecute: TProc;
    FOnAfterExecute: TProc;
  protected
    procedure Execute; override;
  public
    property OnBeforeExecute: TProc read FOnBeforeExecute write FOnBeforeExecute;
    property OnExecute: TProc read FOnExecute write FOnExecute;
    property OnAfterExecute: TProc read FOnAfterExecute write FOnAfterExecute;
  end;

procedure TMyThread.Execute;
begin
  if Assigned(OnBeforeExecute) then
    OnBeforeExecute;
  try
    if Assigned(OnExecute) then
      OnExecute;
  finally
    if Assigned(OnAfterExecute) then
      OnAfterExecute;
  end;
end;

I made it a sealed class so that you cannot replace Execute with something that breaks the design. The added benefit is that you can decouple the thread procedure from the implementing class using events.

like image 153
David Heffernan Avatar answered Oct 05 '22 04:10

David Heffernan


If you want to take care on specific initialization and finalization doing it with events like in David's answer means that you have to assign those events for every thread you create. And that means either adding a specific constructor to pass them in or creating the threads in suspended mode.

Personally I don't really like having to remember to do all those things and would therefore go for a more polymorphic solution:

type
  TInitializeFinalizeThread = class(TThread)
  protected
    procedure InitializeExecution; virtual;
    procedure FinalizeExecution; virtual;
    procedure InternalExecute; virtual;
    procedure Execute; override;
  end;

procedure TInitializeFinalizeThread.Execute;
begin
  InitializeExecution;
  try
    InternalExecute;
  finally
    FinalizeExecution;
  end;
end;

Threads needing to do Ole Stuff could then have a common base that takes care of the initialization and finialization:

type
  TOleThread = class(TInitializeFinalizeThread)
  protected
    procedure InitializeExecution; override;
    procedure FinalizeExecution; override;
  end;

procedure TOleThread.InitializeExecution;
begin
  CoInitialize;
end;

procedure TOleThread.FinalizeExecution;
begin
  CoUninitialize;
end;

This means that classes that are actually going to do something can just inherit from TOleThread and be assured that the initialization and finalization have been taken care of, so they only need to override InternalExecute.

type
  TWordMailMergeThread = class(TInitializeFinalizeThread)
  protected
    procedure InternalExecute; override;
  end;

procedure TWordMailMergeThread.InternalExecute;
begin
  // Whatever you need this to do.
end;

Though they are of course free to override the InitializeExecution and FinalizeExecution methods to set up and quit the connection to the OleServer (Word in this example) instead of doing it in the InternalExecute.

like image 44
Marjan Venema Avatar answered Oct 05 '22 03:10

Marjan Venema