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