Client requests handling.
One of common usage of threads is a client request processing.Server
application should create one or more threads per client in order
to process their requests concurrently without blocking. This
article is dedicated to common way of creating threads that
process messages (or "client requests") sequentially.
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 Size<Count then
begin
Reallocmem(mem,Count*Sizeof(TObject));
Size:=Count;
end;
move(FList.list^,mem^,count*sizeof(pointer));
FList.Count:=0;
end;
// Reset Event
FEvent.ReSetEvent;
finally
FCs.Leave;
end;
Result:=(Count>0);
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
The copyright of the article
Client requests handling. in
Delphi Programming is owned by Lyapin Ilya. Permission to republish
Client requests handling. in print or online must be granted by the author in writing.
Go To Page:
1
2
Articles in this Topic
Discussions in this Topic