0

I need a single background thread for my application . If triggered it will simply do some queries against a SQL Database and update another Table . For the sake of simplicity I built myself a Test project to test out my idea . And I came up with the following code :

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TBackGroundThread = class(TThread)
  protected
    procedure Execute; override;
  end;

type
  TMain = class(TForm)
    Memo1: TMemo;
    btnStartThread: TButton;
    Memo2: TMemo;
    procedure btnStartThreadClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    var
    BackGroundThread : TBackGroundThread;
    SafeToStart : boolean;

    procedure ShowID;
  public
    { Public declarations }
  end;

var
  Main: TMain;

implementation

{$R *.dfm}

procedure TBackGroundThread.Execute;
begin
  Main.SafeToStart:=false;

  Main.ShowID;

  Main.SafeToStart:=true;
end;

procedure TMain.FormCreate(Sender: TObject);
begin
  SafeToStart:=true;
end;

procedure TMain.ShowID;
var i : integer;
begin
  for i := 0 to 10 do
  begin
    sleep(100);
  end;


  Memo1.Lines.Add(IntToStr(BackGroundThread.ThreadID));

  Main.SafeToStart:=true;
end;

procedure TMain.btnStartThreadClick(Sender: TObject);
begin
  if SafeToStart = true then
  begin
    BackGroundThread := TBackGroundThread.Create(false);
    BackGroundThread.FreeOnTerminate:=true;
  end
  else
  begin
    Memo2.Lines.Add('OOPS' +IntToStr(BackGroundThread.ThreadID));
  end;
end;

end.

I am using Private Global Variable to check if it is OK to start or not , I thought this should be safe because I only have one Background task. The Execute will be inside a try except end block.

I tried to check if I have memory leaks , but Delphi did not report any.

My question is :

if I set the FreeOnTerminate property to true, then after the execute the Thread will automatically free itself ?

Thank you.

UPDATE 1

As for the VCL Manipulation from within the thread I will use this :

TThread.Synchronize(TThread.CurrentThread,
procedure
begin
seriesQM.AddXY(qryStat.FieldByName('CurrentPeriod').AsDateTime,qryStat.FieldByName('GesCutQM').AsFloat,
            qryStat.FieldByName('Hour').AsString,$00FF9F40);
end);
user1937012
  • 1,031
  • 11
  • 20
  • 1
    A (non GUI) thread should not modify the GUI. Also be aware that two threads should not use the same SQL connection at the same time. – Olivier Jun 04 '20 at 12:25
  • 1
    The [documentation](http://docwiki.embarcadero.com/Libraries/Rio/en/System.Classes.TThread.FreeOnTerminate) clearly answers your question - yes, if you set `FreeOnTerminate` to true then the TThread instance is automatically freed when the thread terminates. – J... Jun 04 '20 at 13:31
  • 2
    Also, it's always good practice to *not keep a reference* to a TThread that you have set to `FreeOnTerminate` since there is no sensible way to interact with that reference after the thread has started with any guarantee that the reference will still be valid. – J... Jun 04 '20 at 14:19
  • 1
    And even if you fix all of the other errors in the code, your global variable is not safe – David Heffernan Jun 04 '20 at 14:24
  • if I use Critical Section and Lock it before trying to update the Variable ? ( I only run one thread at a time... ) – user1937012 Jun 04 '20 at 14:27
  • 1
    @user1937012 A [`TEvent`](http://docwiki.embarcadero.com/Libraries/Rio/en/System.SyncObjs.TEvent) may also be a suitable synchronization object for your needs. – J... Jun 04 '20 at 14:44
  • 1
    @J... The `TThread.OnTerminate` event can be used to mitigate that issue, allowing the reference to be niled when the thread terminates and before it is freed. – Remy Lebeau Jun 04 '20 at 17:13
  • @RemyLebeau Fair. Part of me still feels that's clumsy, but I don't think I can really articulate why. I suppose it's a reasonable option. – J... Jun 04 '20 at 17:22
  • 1
    TThread.OnTerminate is the perfect place to set SafeToStart := True again. It gets called immediately before the thread frees itself. Your code has one more problem though: you set FreeOnTerminate := True after the thread has already potentially started (because you create it with Suspended = False). In fact, the thread may have not only started, but both started, executed, and finished, before your line FreeOnTerminate:=True gets executed. So you must either set FreeOnTerminate:=True in the thread's constructor, or create the thread suspended, set FreeOnTerminate, and then start the thread. – Matthias B Jun 05 '20 at 06:56

0 Answers0