delphi tcollection

Terms.

What is the “client request“? In this article let’s consider that “client request” is an abstract block of data. How to “process” it? Let’s create an abstract base class for all “client requests” (TILClientData) that declares one abstract method (ProcessData()) which perform actual “processing”. In sample application “data processing” is simple sending some text to memo control.

Test Application

The main idea is to show how to create message queue using critical sections and events. Let’s take example from the “Using threads part 2. TThread class” and rework it.
 
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, ilSyncObj;

type
TILClientData= class(TObject)
protected

    procedure ProcessData;virtual;abstract;

end;

TILClientData1=class(TILClientData)
private

    FRNumber:Integer;

public

    constructor Create(Number:integer);
    procedure ProcessData;override;

end;

TForm1 = class(TForm)

    Memo1: TMemo;
    GroupBox1: TGroupBox;
    btnCreate: TButton;
    btnTerminate: TButton;
    btnPost: TButton;
    procedure FormShow(Sender: TObject);
    procedure btnCreateClick(Sender: TObject);
    procedure btnTerminateClick(Sender: TObject);
    procedure btnPostClick(Sender: TObject);

private

    FThread:TThread;
    procedure EnableButtons;
    procedure OnTerminate(Sender:TObject);

end;

TMyThread=class(TThread)
private

    FCS:TILCriticalSection;
    FEvent:TILEvent;
    FList:Tlist;
    procedure DoProcessData(Item:TObject);

protected

    procedure Execute;override;

public

    procedure terminate;
    procedure AddToProcess(Item:TObject);
    constructor Create;
    destructor Destroy;override;

end;

var

    Form1: TForm1;

implementation
{$R *.DFM}

// TMyThread
type

    TObjectsList = array[0..MaxListSize-1] of TObject;
    PObjectsList =^TObjectsList;

constructor TMyThread.Create;
begin

    inherited Create(true);
    FCS:=TILCriticalSection.Create;
    FEvent:=TILEvent.Create(nil,true,false,'');
    FList:=TList.Create;

end;

destructor TMyThread.Destroy;
begin

    FCS.free;
    FEvent.free;
    FList.Free;
    inherited Destroy;

end;

procedure TMyThread.Execute;
var

    Size,i,Count:integer;
    mem:PObjectsList;

    function GetQueData(Timeout:DWORD):boolean;
    begin

        Result:= FEvent.WaitFor(Timeout);

        if not Result then Exit;

        FCS.Enter;
        try

            Count:=FList.Count;
            if Count>0 then
            begin

                if Size0);

    end;

begin
try

    Size:=0;mem:=nil;
    Form1.Memo1.Lines.Add('Begin execution');
    while not Terminated do
    begin

        if GetQueData(Infinite) then
        begin

            repeat

                for i:=0 to Count-1 do
                DoProcessData(mem^[i]);

            until not GetQueData(0);

        end;

    end;
    FreeMem(mem);
    Form1.Memo1.Lines.Add('Finish execution');

except on e:Exception do
begin

    ShowMessage('Unhandled exception in TMyThread.Execute:
'+e.ClassName+' '+e.Message+#13'Appliction wil be terminated');
    ExitProcess(1);

end;
end;
end;

procedure TMyThread.terminate;
begin
FCs.enter;
try

    inherited terminate;
    FEvent.setEvent;

finally

    FCs.leave;

end;
end;

procedure TMyThread.AddToProcess(Item:TObject);
begin

    Form1.Memo1.Lines.Add('Adding item #'
+Inttostr(TILClientData1(Item).FRNumber));
    fCS.Enter;
    try

        FList.Add(Item);
        FEvent.SetEvent;

    finally

        FCS.Leave;

    end;

end;

procedure TMyThread.DoProcessData(Item:TObject);
begin

    TILClientData(Item).ProcessData;
    Item.Free;

end;

// TForm1
procedure TForm1.EnableButtons;
begin

    btnCreate.Enabled:=not Assigned(FThread);
    btnTerminate.Enabled:= Assigned(FThread);
    btnPost.Enabled:=Assigned(FThread);

end;

procedure TForm1.FormShow(Sender: TObject);
begin
EnableButtons;
end;

procedure TForm1.btnCreateClick(Sender: TObject);
begin

    FThread:=TMyThread.Create;
    FThread.OnTerminate:=OnTerminate;
    EnableButtons;
    FThread.Resume;

end;

procedure TForm1.btnTerminateClick(Sender: TObject);
begin
FThread.Terminate;
end;

procedure TForm1.OnTerminate(Sender: TObject);
begin
FThread:=nil;
EnableButtons;
end;

procedure TForm1.btnPostClick(Sender: TObject);
begin
if Assigned(FThread) then
begin
TMyThread(FThread).AddToProcess(TILClientData1.Create(Random(100)));
end;
end;

{ TILClientData1 }
constructor TILClientData1.Create(Number: integer);
begin
FRNumber:=Number;
end;

procedure TILClientData1.ProcessData;
begin
Form1.Memo1.Lines.Add(IntTostr(FRNumber)+' Processed');
sleep(Random(5000));
end;

Code explanation

For explanation TForm1 class see “Using threads part 2. TThread class” article. There is only addition: button “Post to process” which allows posting some “client request” to further processing. TILEvent and TILCriticalSection from module ilSyncObjs are described in previous articles. TILClientData class represents an abstract “client request” – base class for all “requests”. TILClientData1 is some actual client request and overrides ProcessData method. The only processing is adding text to memo control. TMyThread class inherited from TThread represents new thread. TMyThread.Execute waits on the FEvent event until it’s set to signaled state and after that retrieves objects of type TILClientData from the critical section protected list. Then list is empty the event is reset. This method also periodically checks Terminated property and exits when it’s True. The Terminate method of TMyThread is overridden. In addition to inherited Terminate method call, it also sets Fevent event to signaled state to “awake” running thread. And finally, AddToProcess method is always called in context of the main thread. It simple add “request” objects to the critical section protected list. The program is written for Borland Delphi 4, but this code must work under Delphi 2/3/4/5. You may experiment with the program for better understanding. I would like to answer any questions that you may have.