2

I have to develop a program to keep watching values in databases based on a Select statement given by me

the watched values can be changed at anytime and my program must sense the changes based on a result of the select statement which is given by me

i want to watch the select result by using TThread because my system is also has another features and the user need to work on it not only to watch values.

how to do it using TThread in Delphi XE2

am using VCL...no .Net

Regards.

user1512094
  • 611
  • 2
  • 10
  • 23
  • From the thread, call `TThread.Synchronize` to update the UI. This runs that updating code on the main UI thread. – David Heffernan Jul 09 '12 at 13:42
  • 2
    With Delphi comes a simple thread-application which shows very nicely how to use Synchronise to update a UI-control in the main-thread. – Andreas Jul 09 '12 at 14:44
  • 2
    OK. So which part of the task are you having trouble with? "How to do it" is not specific enough. – Rob Kennedy Jul 09 '12 at 22:08
  • @David, In the case of populating a Listbox with items, there is no need to synchronize with the main thread as the Listbox uses SendMessage to "fill itself" with items. This is one of the rare exceptions when no explicit synchronization with the main thread is necessary. – iamjoosy Jul 10 '12 at 10:10
  • @iamjoosy I don't think so. What if accessing the list box `Handle` leads to `RecreateWnd`. At that point the list box has affinity with the wrong thread. – David Heffernan Jul 10 '12 at 10:35
  • @David, under what circumstances would accessing the list box Handle lead to RecreateWnd? – iamjoosy Jul 10 '12 at 11:31
  • @iamjoosy Sorry, I meant `CreateHandle`. – David Heffernan Jul 10 '12 at 11:36
  • @David, my last question was rather referring to the fact that I can't see under which circumstances the window handle would be recreated. And since I can't see that, I think that populating a list box from a Thread is perfectly safe. – iamjoosy Jul 10 '12 at 12:26
  • @iamjoosy There are oodles of ways for RecreateWnd to be called for a list box. – David Heffernan Jul 10 '12 at 13:22

1 Answers1

3

[Edit]

Improved answer, now thread runs continuously and "keep watching values".

Let's build a sample.

First, Create new VCL app. Drop on the form one TListBox and two TButton component. You need to write button click handlers and add one private method. Entire unit should look like this:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    FCollector: TCollector;
    procedure OnCollect(S: TStrings);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(FCollector) then Exit;
  FCollector := TCollector.Create;
  FCollector.OnCollect := Self.OnCollect;
  FCollector.Start;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if not Assigned(FCollector) then Exit;
  FCollector.Terminate;
  FCollector := nil;
end;

procedure TForm1.OnCollect(S: TStrings);
begin
  ListBox1.Items.AddStrings(S);
end;

end.

Next we should add our thread, select from menu: File -> New unit and replace with code:

unit Unit2;

interface

uses
  System.Classes;

type

  TCollectEvent = procedure (S: TStrings) of object;

  TCollector = class(TThread)
  private
    { Private declarations }
    FTick: THandle;
    FItems: TStrings;
    FOnCollect: TCollectEvent;
    FInterval: Integer;
  protected
    procedure Execute; override;
    procedure DoCollect;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Terminate;
    property Interval: Integer read FInterval write FInterval;
    property OnCollect: TCollectEvent read FOnCollect write FOnCollect;
  end;

implementation

uses Windows, SysUtils;

{ TCollector }

constructor TCollector.Create;
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FInterval := 1000;
  FTick := CreateEvent(nil, True, False, nil);
end;

destructor TCollector.Destroy;
begin
  CloseHandle(FTick);
  inherited;
end;

procedure TCollector.DoCollect;
begin
  FOnCollect(FItems);
end;

procedure TCollector.Terminate;
begin
  inherited;
  SetEvent(FTick);
end;

procedure TCollector.Execute;
begin
  while not Terminated do
  begin
    if WaitForSingleObject(FTick, FInterval) = WAIT_TIMEOUT then
    begin
      FItems := TStringList.Create;
      try
        // Collect here items
        FItems.Add('Item ' + IntToStr(Random(100)));
        Synchronize(DoCollect);
      finally
        FItems.Free;
      end;
    end;
  end;
end;

end.

Now, when you press Button1, you will receive from thread Items in your combo. Pressing Button2 thread stops executing.

You should take in account:

  1. Set Interval to watch values, default is 1000ms, see interval property;

  2. All your code (include DB access components) inside Execute and related to it should be thread-save.

Marcodor
  • 4,578
  • 1
  • 20
  • 24
  • What do you mean by saying "All your code (include DB access components) inside Execute and related to it should be thread-save"? – user1512094 Jul 10 '12 at 09:36
  • Some notes about ADO: http://stackoverflow.com/questions/3266532/ok-to-use-tadoconnection-in-threads – Marcodor Jul 10 '12 at 10:47
  • Hi, in the like you provided (http://stackoverflow.com/questions/3266532/ok-to-use-tadoconnection-in-threads) they wrote that each thread need its own connection which must be created within the thread execuation...but in my case I have only one connection since all of the requested records are located in one database but in different tables so in my case each thread must have its own ADODataSet and these datasets can be connected to one ADOConnection!...doesn't it work in this way? I have tried it and nothing wrong... – user1512094 Jul 10 '12 at 12:46
  • Also i noticed something weird which is when i terminate the thread it doesn't interrupt the execution until it finishes after that it stops working...what should i call to stop the execution of the thread at any point even if the process was not finished yet...thank you. – user1512094 Jul 10 '12 at 12:47
  • To terminate thread, you should call Thread.Terminate; see Button2Click in answer. – Marcodor Jul 10 '12 at 13:06
  • i've tried doing : Thread.Terminate; but my problem is that when I call this (Thread.Terminate) it doesn't stop directly and it continues until the end of the execution then it gets terminated...what i need is to stop it before finishing the execution of the thread. – user1512094 Jul 10 '12 at 18:44
  • @user1512094, it shouldn't. Terminate as shown in Unit2 call SetEvent, this invoke WaitForSingleObject and thread terminates imediatelly. – Marcodor Jul 10 '12 at 18:55