Mega Search
23.2 Million


Sign Up

Make a donation  
Console Application - TThreads and TLists  
News Group: embarcadero.public.delphi.oodesign

I have a question regarding console, multithreading with objects and multiple units.  I am using Delphi XE6.

Any help is greatly appreciated and thanks in advance for your time.

I have  a VCL program using many of these routines and it works as expected.  However, I want to speed up a time consuming calculation process and convert the calculation component to a multi-threaded console program.  

For the conversion  I have made sure any writes to global memory is thread safe.  I have permitted multiple reads from the threads to a large global TList , upwards of 500,00 elements but now writes to this list.  In addition all special mathematical calculations on the data are now included with the objects created.

This is the typical inheritance layout

TSlice_Math = Class( TObject);
TSlice_It = Class(TSlice_Math);  
TSlice_Calculate_it = Class(TSlice_it)

I create a TList object in the TSlice_it class (not in global memory but created in the object) and this works well and as expected in that class. 
 
However in the TSlice_Calculate_it, a class inherited from TSlice_it the Tlist object count is always zero and none of the tlist items are visible.  

Thus the question is why the TList object does not appear in the TSlice_Calculate_it class?

The following is a general outline of the code used...

The main thread uses the sleepsort demo code to create the threads.

In the main thread I call

  // Create critical section and threads
  Lock := TCriticalSection.Create;
  for I := 0 to ArrLen - 1 do
  begin
    Threads[I] := TSliceCalc.Create(I, Lock);
    Handles[I] := Threads[I].Handle;
 end; 
  // Wait until threads terminate
  // This may take up to ArrLen - 1 seconds
  WaitForMultipleObjects(ArrLen, @Handles, True, INFINITE);

This works fine and opens up multiple threads .

In the TSliceCalc.execute I have

procedure TSliceCalc.Execute;
var
  Sliced:  TSlice_Calculate_it;
  x1,y1,x2,y2:  double
begin 
  FLock.Acquire;
   // A process here to get an index and increment in global memory
  FLock.Release; 
  // A process in here to get the values for X1,x2,y1,y2 from global memory using the index   
  Sliced:= TSlice_Calculate_it.create(x1,y1,x2,y2);;
  ....
end;


// This Object calculates various parameters based on a Tlist object created by TSlice_it.

TSlice_Calculate_it = Class(TSlice_It)
....
constructor Create(x1,y1,x2,y2:  double);
.....
Constructor TSlice_Calculate_it.Create(x1,y1,x2,y2:  double);
begin
  Inherited create(x1,y1,x2,y2:  double);
end;

// This Object calculates and generates a TList 

TSlice_it = Class(TSlice_It)
  Terrain:  TList;
....
constructor Create(x1,y1,x2,y2:  double);
.....
Constructor TSlice_it.Create(x1,y1,x2,y2:  double);
begin
  Inherited create;
  Terrain:= TList.Create;
  .... Code to generate the Terrain in the TList based on reading and analysing the global TList.
end;

As a final observation the code runs (well at least the TSlice_it terrain list generation component works.  However there was no reduction in the overall time to process with multiple threads, in fact it was slightly slowing using multiple threads.  This suggests that process is sequential, even though multiple threads were used.

Vote for best question.
Score: 0  # Vote:  0
Date Posted: 12-May-2014, at 8:49 PM EST
From: Mark SIMPSON
 
Re: Console Application - TThreads and TLists  
News Group: embarcadero.public.delphi.oodesign
Hi Wayne,

Thanks for the reply.  

1_______________________
The multithreading was the second part of the post, but I will reiterate below.

> 
> Nowhere in your sample code do I see any reference to a TThread object. How 
> are you thinking any of this is multithreaded?
> 
> -- 
> Wayne Niddery
> "You know what they call alternative medicine that has been proven to work? 
> Medicine." - Tim Minchin


The main program uses this to create the threads........Borrowed from Embarcadero SleepSort demo
http://docwiki.embarcadero.com/CodeExamples/XE5/en/SleepSort_%28Delphi%29


// Create critical section and threads
Lock := TCriticalSection.Create;
for I := 0 to ArrLen - 1 do
begin
Threads[I] := TSliceCalc.Create(I, Lock);
Handles[I] := Threads[I].Handle;
end;
// Wait until threads terminate
WaitForMultipleObjects(ArrLen, @Handles, True, INFINITE);


The Thread works like this......Also borrowed from Embarcadero Sleep Sort Demo, just renamed.

TSliceCalc = Class(TThread);
.....

procedure TSliceCalc.Execute;
var
Sliced: TSlice_Calculate_it;
x1,y1,x2,y2: double
begin
FLock.Acquire;
// A process here to get an index and increment in global memory
FLock.Release;
// A process in here to get the values for X1,x2,y1,y2 from global memory using the index
Sliced:= TSlice_Calculate_it.create(x1,y1,x2,y2);;
.....
end;

i.e. The thread creates an object of the type TSlice_Calculate_it.


So that is the multi-threaded part of your response.

2_______________________________________________

The actual generation of all the threads is working well, the issue I have is with the object TSlice_Calculate_it created by the thread and it's handling of Tlist. 

As mentioned above I have traditionally used a TList in global memory for storage of short term objects, and that was OK for a single thread.    However, since I am now using multiple threads I decided to include the thread generated TList in the object created by the tthread.  That makes sense since only that thread needs that Tlist and it is disposed after the object is closed by the thread.  

Since my post I have investigated the use of a dynamic array in lieu of the TList and that seems to work fine, i.e the dynamic array elements are  visible to the descendents but not the TList objects.  

I thought I must be missing something obvious regarding TLists and/or creation of objects by threads.

3______________________________________________

> TSlice_Calculate_it inherits from TSlice_It, but in the code below it
> appears TSlice_Calculate_it is creating instances of TSlice_It. Why? This
> doesn't make sense to me.

The thread creates an object of type TSlice_Calculate_it which is a descendant of TSlice_it which is in turn a descendant of  TSlice_math and all of this performs as expected.  

The Tlist is created byTSlice_it.   Finally I there are other descendants of TSlice_it  as well as TSlice_math so it is not viable to simply combine all into one.

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 13-May-2014, at 1:46 AM EST
From: Mark SIMPSON
 
Re: Console Application - TThreads and TLists  
News Group: embarcadero.public.delphi.oodesign
Ok,  Here I am eating a nice large slice of Humble Pie.  

Everything works including TList created by objects.  The issue was very minor.  I had commented out a line of code that stopped an automatic deletion of the TList contents.
So all is good in the world of multi-threading. 
However, there appears to be many issues to solve to increase speed since I am not observing any increase in performance after multi-threading, i.e only using 17% of available processor cycles, I was expecting to max out processor cycles.  I believe it is to do with creating and closing my objects so I'll investigate ways to leave them created and just insert the new data.

Thanks every one

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 13-May-2014, at 7:21 AM EST
From: Mark SIMPSON
 
Re: Console Application - TThreads and TLists  
News Group: embarcadero.public.delphi.oodesign
Wayne,

Thanks for the reply.

I prepared a response to this yesterday but site went down for maintenance mid post.

> {quote:title=Wayne Niddery wrote:}{quote}
> "Mark SIMPSON" wrote in message news:674180@forums.embarcadero.com...
> >
> > TSliceCalc = Class(TThread);
> 
> 
> This is different than you first posted, or maybe I'm mixing up your class 
> names.
> 
> For performance, are you calling Synchronize anywhere? How about SendMessage 
> instead of PostMessage?
> 
> 
> -- 
> Wayne Niddery
> "You know what they call alternative medicine that has been proven to work? 
> Medicine." - Tim Minchin

No I did not include that statement previously.

I have investigated performance issues and identified at least one culprit.I regularly use the following call of the nature if  Pos(GType, 'G') <>0, perhaps on 50  occasions in one of the objects various subroutines.  When I test that routine alone in a multi-threaded stress testing program (i wrote since my first post) I found the maximum performance CPU usage was about 17%.  However when I replace with an integer based call If (i = 0) , I can max out CPU usage at 100%.  Also all the mathematical functio
ns appear to be also to be able to use 100% CPU.  I understand this is called a blocking call.  So I will go through the program to replace any calls that don't pass the simple stress test.  I wrote the original code back in 1993 (Turbo Pascal for Windows) and upgraded with every revision of Delphi, but now with multi-threading I need to address another raft of issues.

BTW all the threads are designed for virtually zero interaction with the main thread, only one to get the calculation index position protected by a critical section.  Otherwise only reads of global memory.

Cheers

Mark

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 14-May-2014, at 6:53 PM EST
From: Mark SIMPSON