chrb
asked on
Hand out TTables
Hi,
I am going to write a ISAPI extension which is performing some database actions. The problem is that I dosn't want to create a new TTable for each request since that would slow down the requests to much. And one TTable would be too few since some request could be lengthy. What I was wondering was how to make it like each request asks for a TTable, and wait if it isn't some free. If there is an free the request will get it.
Is it something I have to remeber when doing this?
Hope somebody understand what I ment.
Thanks
With best regards
Christian Bøhn
I am going to write a ISAPI extension which is performing some database actions. The problem is that I dosn't want to create a new TTable for each request since that would slow down the requests to much. And one TTable would be too few since some request could be lengthy. What I was wondering was how to make it like each request asks for a TTable, and wait if it isn't some free. If there is an free the request will get it.
Is it something I have to remeber when doing this?
Hope somebody understand what I ment.
Thanks
With best regards
Christian Bøhn
ASKER
I will take a look at it, but as far as I see it isn't thread safe. I forgot to meantion that this was going to be access by a lot of threads. But it shouldn't be hard to add some critical sections?
Chr
Chr
Hi,
You are surely right. Here is a critical section I wrote before. If you can, the threads and criticals section objects be in the same unit: so in your thread unit declare
...
implementation
Uses
Sync;
var
MyCs : TCriticalSection;
...
initialization
MyCs := TCriticalSection.Create;
finalization
MyCs.Free;
end.
THEN IN YOUR THREADS CODE DO:
procedure ThreadMain...
begin
...
if NeedsProcessing then
begin
MyCs.Lock;
MyTable := GetIdleTable;
MyCs.UnLock;
end;
...
if FinishedProcessing then
begin
MyCs.Lock;
ReleaseTable(MyTable);
MyCs.Unlock;
end;
..
end;
THIS IS THE CODE FOR SYNC IF YOU THERE RESPOND PLEASE
unit Synch;
interface
uses
Windows;
type
TCriticalSection = class(TObject)
private
GlobalCriticalSection : TRTLCriticalSection;
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure UnLock;
end;
// By now only named mutexes are supported
TMutex = class(TObject)
private
HMutex : THandle; // This one is mutex handle
HandleValid : boolean;
FName : string; // This one is mutex name
public
constructor Create(AName : string);
destructor Destroy; override;
procedure Lock;
procedure UnLock;
end;
implementation
uses
SysUtils;
constructor TCriticalSection.Create;
begin
InitializeCriticalSection( GlobalCrit icalSectio n);
end;
destructor TCriticalSection.Destroy;
begin
DeleteCriticalSection(Glob alCritical Section);
inherited Destroy;
end;
procedure TCriticalSection.Lock;
begin
EnterCriticalSection(Globa lCriticalS ection);
end;
procedure TCriticalSection.UnLock;
begin
LeaveCriticalSection(Globa lCriticalS ection);
end;
constructor TMutex.Create(AName : string);
var
Str : array[0..255] of char;
begin
FName := AName;
HandleValid := false;
StrPCopy(Str, FName);
HMutex := CreateMutex(
nil, // security NONE
false, // no initial owner
Str); // mutex name
if (HMutex = NULL) then
begin
end;
end;
destructor TMutex.Destroy;
begin
// Do nothing since mutexes automatically deleted
inherited Destroy;
end;
procedure TMutex.Lock;
var
dwWaitResult : DWORD;
begin
// wait for ovnership time out 0 by now
dwWaitResult := WaitForSingleObject(HMutex , INFINITE);
case dwWaitResult of
WAIT_OBJECT_0 : HandleValid := true;//OK
WAIT_TIMEOUT: ;
WAIT_ABANDONED: ;
end;
end;
procedure TMutex.UnLock;
begin
if HandleValid then
begin
ReleaseMutex(HMutex);
HandleValid := false;
end;
end;
end.
You are surely right. Here is a critical section I wrote before. If you can, the threads and criticals section objects be in the same unit: so in your thread unit declare
...
implementation
Uses
Sync;
var
MyCs : TCriticalSection;
...
initialization
MyCs := TCriticalSection.Create;
finalization
MyCs.Free;
end.
THEN IN YOUR THREADS CODE DO:
procedure ThreadMain...
begin
...
if NeedsProcessing then
begin
MyCs.Lock;
MyTable := GetIdleTable;
MyCs.UnLock;
end;
...
if FinishedProcessing then
begin
MyCs.Lock;
ReleaseTable(MyTable);
MyCs.Unlock;
end;
..
end;
THIS IS THE CODE FOR SYNC IF YOU THERE RESPOND PLEASE
unit Synch;
interface
uses
Windows;
type
TCriticalSection = class(TObject)
private
GlobalCriticalSection : TRTLCriticalSection;
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure UnLock;
end;
// By now only named mutexes are supported
TMutex = class(TObject)
private
HMutex : THandle; // This one is mutex handle
HandleValid : boolean;
FName : string; // This one is mutex name
public
constructor Create(AName : string);
destructor Destroy; override;
procedure Lock;
procedure UnLock;
end;
implementation
uses
SysUtils;
constructor TCriticalSection.Create;
begin
InitializeCriticalSection(
end;
destructor TCriticalSection.Destroy;
begin
DeleteCriticalSection(Glob
inherited Destroy;
end;
procedure TCriticalSection.Lock;
begin
EnterCriticalSection(Globa
end;
procedure TCriticalSection.UnLock;
begin
LeaveCriticalSection(Globa
end;
constructor TMutex.Create(AName : string);
var
Str : array[0..255] of char;
begin
FName := AName;
HandleValid := false;
StrPCopy(Str, FName);
HMutex := CreateMutex(
nil, // security NONE
false, // no initial owner
Str); // mutex name
if (HMutex = NULL) then
begin
end;
end;
destructor TMutex.Destroy;
begin
// Do nothing since mutexes automatically deleted
inherited Destroy;
end;
procedure TMutex.Lock;
var
dwWaitResult : DWORD;
begin
// wait for ovnership time out 0 by now
dwWaitResult := WaitForSingleObject(HMutex
case dwWaitResult of
WAIT_OBJECT_0 : HandleValid := true;//OK
WAIT_TIMEOUT: ;
WAIT_ABANDONED: ;
end;
end;
procedure TMutex.UnLock;
begin
if HandleValid then
begin
ReleaseMutex(HMutex);
HandleValid := false;
end;
end;
end.
Sorry, I think you create a thread when one request comes and needs processing. Them complete thread can be:
procedure TMyThread.Execute;
var
Done : boolean;
NeedsProcessing : boolean;
FinishedProcessing : boolean;
begin
Done := false;
NeedsProcessing := true;
FinishedProcessing := false;
while not Done do
begin
if NeedsProcessing then
begin
MyCs.Lock;
MyTable := GetIdleTable;
if MyTable <> nil then
begin
// Process the table here
// Signal For release
NeedsProcessing := False;
FinisgedProcessing := True; // querry to release table
end;
MyCs.UnLock;
end;
if FinishedProcessing then
begin
MyCs.Lock;
ReleaseTable(MyTable);
MyCs.Unlock;
Done := true; //terminate thread
end;
end; //while not done
end;
Please notify me if you can I should leave in 20 mins
Igor
procedure TMyThread.Execute;
var
Done : boolean;
NeedsProcessing : boolean;
FinishedProcessing : boolean;
begin
Done := false;
NeedsProcessing := true;
FinishedProcessing := false;
while not Done do
begin
if NeedsProcessing then
begin
MyCs.Lock;
MyTable := GetIdleTable;
if MyTable <> nil then
begin
// Process the table here
// Signal For release
NeedsProcessing := False;
FinisgedProcessing := True; // querry to release table
end;
MyCs.UnLock;
end;
if FinishedProcessing then
begin
MyCs.Lock;
ReleaseTable(MyTable);
MyCs.Unlock;
Done := true; //terminate thread
end;
end; //while not done
end;
Please notify me if you can I should leave in 20 mins
Igor
Or in simplified form:(actually there is a bug in prev version:
procedure TMyThread.Execute;
var
Done : boolean;
NeedsProcessing : boolean;
begin
Done := false;
NeedsProcessing := true;
while not Done do
begin
if NeedsProcessing then
begin
MyCs.Lock;
MyTable := GetIdleTable;
MyCs.UnLock; //unlock so other can acquire it
if MyTable <> nil then // if nil wait for next pass
begin
// Process the table here
........................
// Signal For release
NeedsProcessing := False;
MyCs.Lock;
ReleaseTable(MyTable);
MyCs.Unlock;
Done := true; //terminate thread
end; // if table...
end; // if needs...
end; //while not done...
end;
Regards,
Igor
procedure TMyThread.Execute;
var
Done : boolean;
NeedsProcessing : boolean;
begin
Done := false;
NeedsProcessing := true;
while not Done do
begin
if NeedsProcessing then
begin
MyCs.Lock;
MyTable := GetIdleTable;
MyCs.UnLock; //unlock so other can acquire it
if MyTable <> nil then // if nil wait for next pass
begin
// Process the table here
........................
// Signal For release
NeedsProcessing := False;
MyCs.Lock;
ReleaseTable(MyTable);
MyCs.Unlock;
Done := true; //terminate thread
end; // if table...
end; // if needs...
end; //while not done...
end;
Regards,
Igor
ASKER
I am going to access it from ISAPI extension. But will the database actions take place in the thread called from, or in the thread created ? And I heard something about database sessions when using it in theads? I will try you code. Thanks!
Chr
Chr
Hi,
I really do not program with ISAPI. But done something very much like above with RPC. So the code above is a bit elaborated pseudo-code, there may be some bugs but the idea is there. By now thats all I can.
Have a nice day
Igor
I really do not program with ISAPI. But done something very much like above with RPC. So the code above is a bit elaborated pseudo-code, there may be some bugs but the idea is there. By now thats all I can.
Have a nice day
Igor
ASKER
Anyway you have given me a very good push. So send a message as anwear.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
If I can understand, Say we have a N resources (TTables on our form-or you may prefer to create them dynamically-) For being clear lets say we have 4 tables for performing DB operations. Lets call these as Table1, Table2, Table3, Table4. We do the following:
1 - On creation of the application store them in a list and mark their states:(lets say it is var TableList : TStringList;
..
TableList := TStringList.Create;
TableList.AddObject('Idle'
TableList.AddObject('Idle'
TableList.AddObject('Idle'
TableList.AddObject('Idle'
..
2 - When a request is made check for them and return one of the idle tables:
function Form1.GetIdleTable:TTable;
var
i : integer;
begin
Result := nil; // assume not found
for i:=0 to TableList.Count-1 do
if TableList[i] = 'Idle' then
begin
Result := TTable(TableList.Objects[i
TableList[i] := 'Busy';
Break;
end;
end;
3 - Write down a method so that sub tasks can release the table when finished their tasks
procedure Form1.ReleaseTable(T : TTable);
var
i : integer;
begin
for i:=0 to TableList.Count-1 do
if TableList.Objects = Pointer(T) then
begin
TableList[i] := 'Idle';
Break;
end;
end;
4 - On caller of the method in step 3, wait until an idle table returns
...
MyTable := GetIdleTable;
while MyTable = nil do MyTable := GetIdleTable;
// now use MyTable for the querry
...
5 - After using a the table a process should release it as follows:
...
// Assume we have done with MyTable obtained in step 4
ReleaseTable(MyTable);
...
Is this ok?
Regards
Igor