Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations MikeeOK on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

using beginthread

Status
Not open for further replies.

vitalstates

Programmer
Jan 15, 2006
6
GB
Can anyone suggest a way of setting freeonterminate when using the beginthread method in delphi. I launch a number of threads from my main form but have no way of knowing when they have finished so consequently cant free the handle that beginthread uses.

I have got around the above problem by using a standard TThread but when I create a canvas in the thread using the parent dc and then subsequently free it the GDI handle count(as shown in process explorer) still gets incremented. This means that if the program creates many threads over time then it will run out of GDI handles.
 
Can you post some code? I cant see why the standard threads are not freeing the resources.

Using the BeginThread API function will not set the multi thread environment flag, eventually making weird things happen.

buho (A).
 
thanks for responding buho....code as follows

procedure Objthread.Execute;
var
ufocanvas : TCanvas;
ct,tix,tiy : integer;
begin
ufocanvas := TCanvas.Create; // new canvas
tix := ufo[objid].ufox;
tiy := ufo[objid].ufoy;

try
ufocanvas.Handle := mydc;
for ct := 255 downto 47 do
begin
ufocanvas.Pen.Color := rgb(0,ct,0);
ufocanvas.Brush.Color := rgb(0,ct,0);
ufocanvas.Ellipse(tix,tiy,tix+8,tiy+4);
sleep(40);
end;
finally
ufocanvas.Refresh;
ufocanvas.Free;
end;
end;

launch code creates suspended
then sets freeonterminate
then resumes the thread

monitoring with process explorer shows that this execute increments GDI handles by 1 every time it is submitted.
mydc is a global device context which is the same as the one used by the parents canvas.

thanks

Ed
 

You are not locking the canvas.

How are you checking the code? Is this the only one thread you have running (including the main one) which uses myDC while making the test?

buho (A).
 
Hi buho

There is one other thread that runs for the life of the application. This thread also draws on a canvas using Mydc. To clarify, there is the main app thread, the main draw thread and many short lived(10 seconds or so) threads as shown in the code previously.

I have used process explorer
which indicates that the main draw thread is only using 1 handle and 1 GDI handle. When I launch the short lived threads they use one handle which they release at close, and 1 GDI handle which they dont seem to release.

Thanks

Ed
 
drawing should only occur in the main thread since TCanvas is not threadsafe.

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 

Hello, Daddy! Nice to meet you again. :)

Vital: may be the leak is do to the fact you are not locking the canvas before drawing. It is said that multi-thread writing to a DC without synchronization can lead to resource leak (it WILL lead to AVs sooner or later, that for sure).

Efficience wise, why creating a TCanvas every time you start a volatile thread? Use a global object with canvas (TPaintBox?) and access it from your threads with due synchronization (lock/trylock methods and critical sections).

buho (A).

 
Thanks buho

Maybe I dont understand enough about delphis use of canvas. I wrote a screensaver in C++(which worked fine) some years ago and I was trying to reproduce it in delphi.
One thread is continually drawing to the full screen canvas all the time and the little threads periodically pop up and draw on the same dc using a new canvas.
I thought I had to do it this way because the little temporary threads obviously have different pen and brush to the continuously running threads
It sounds like I've got to brush up on the delphi canvas thing.

btw any advice on my original question..i.e how to access the thread object when using begin thread....beginthread returns an integer which is the handle of the thread...can that handle be used to access the thread object???

thanks

Ed
 
I seem to have stumbled on the answer to my own question - for interest.....

setting freeonterminate in a thread may release the storage resources but it doesnt release the GDI handle if it used one. By setting the object variable to nil after thread.destroy the GDI handle is released......For the moment I find this behaviour a bit bizarre...oh well c'est la vie.

many thanks to all who offered thoughts

Ed
 
setting freeonterminate in a thread may release the storage resources but it doesnt release the GDI handle if it used one. By setting the object variable to nil after thread.destroy the GDI handle is released


Sorry but nope. Your thread object is not creating any GDI handle, the canvas object is; and the canvas is freed in the Execute method.

Furthermore, niling a pointer to a freed objetc will do nothing. At that point, the objects is destroyed and freed, you are niling a pointer pointing to nowhere. No action is carried over the former object (which does not exist anymore).

What you have here is a synchronization problem and/or a memory assignment problem in the code not posted here. In both cases, little changes in the code can lead to changes in the general behaviour, but you are no solving the problem, only masking it.

As said before: you need to lock the DC (canvas method Lock) to multi-thread write to it.

btw any advice on my original question..i.e how to access the thread object when using begin thread....beginthread returns an integer which is the handle of the thread...can that handle be used to access the thread object???


Without creating a TThread object you have not a TThread object to free.

TThread is a shell for an OS thread. Any TThread is associated with an OS thread (that is why it owns a thread handle), but in the case you are asking the handle returned by BeginThread is not associated with a TThread object.

Think this way: if you create a TForm, that TForm is associated to a OS window. On the other way, if you create an OS window (using the API directly) you have not a TForm associated with it.

buho (A).




 
I agree 100% with you Buho,
Vitalstates, your code is not complete, you must aquire the main canvas device context and release it afterwards.

one good example of this can be found here :

anyway, I still think this is generally a bad idea doing graphics like this, try to stick with one thread as the TCanvas VCL object is not threadsafe. Ignoring this can lead to strange effects (race problems, resource leaks and so on...). if you still insist on doing this, there are some samples available on the net.

here is how to do it :

the same sample can be found in the delphi demos folder

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Many thanks for the pointers guys. I have had a look through the examples and they are exactly what I have been searching the net for for the last week...
I have spent a week wishing there were a function 'RELEASEDC' and also 'GETDESKTOPWINDOW'. Neither of these are mentioned in my help files(Delphi 7 Pro) so I have been storing the mainwindow handle as a global and just freeing the canvas hoping it would give the gdi handle back....
oh well....I am running now with no leakage and no sync problems(so far......have run continuously for last 14 hours)

many thanks again

Ed
 
happy to help you :)

I have spent a week wishing there were a function 'RELEASEDC' and also 'GETDESKTOPWINDOW'. Neither of these are mentioned in my help files(Delphi 7 Pro) so I have been storing the mainwindow handle as a global and just freeing the canvas hoping it would give the gdi handle back....

the answer is very simple, these are not delphi functions but WIN32 API functions, so for detailed help on these go here :
Cheers,
Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Daddy:

As per the code shown here, he have a DC (myDC) which he is assigning to the canvas.

The code in Dr. Bob is the thread demo application (Delphi "Demos" folder). IMHO, that demo is very old, dating from the times where Borland had not added the TCanvas.Lock method (not sure about this, only a feeling). Note the guy is talking about a "closer look at Delphi 2".

My point is: you are multi-threading for speed. Lock is thousand times faster than Synchronize (or at least hundreds times faster).

When the thread work is mainly computation and little drawing (like in the demo), using the "Synchronize" method approach is not a problem; but if the thread work is mainly drawing, your are defeating the multi-threading and putting all your work in the main thread (and adding the sensible overhead of "Synchronize" itself).

Well... I agree in that for a serious drawing work you'll need to buffer your drawing, making the thread computation bounded and not drawing bounded and reducing the drawing to a handfull of bitblts, but I still do not like "Synchronize" :).

Vital:

GetDC, GetDesktopWindow and the like are API functions; you'll not find them in the Delphi help but in the API help.

buho (A).


 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top