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.