Page 1 of 2

Threads - Self Terminating?

Posted: Sat May 15, 2004 9:19 am
by dmoc
Win98 - If a thread-based procedure completes it's task and exits normally (or via a ProcedureReturn) shouldn't the thread itself be deleted? I'm talking about the thread/s as reported by a process monitoring utility. As far as PB is concerned everything appears ok but if your prog keeps launching threads then the number of threads reported by the system just keeps increasing. They *do* get cleaned out when the prog exits but this is no good for a prog that is intended to run continuously. I'm trying to avoid KillThread.

Posted: Sat May 15, 2004 9:39 am
by fweil
...,

Threads do not close alone, and you have to be careful to clean by yourself if you write a prog running threads, otherwise it may be some memory not freed.

Depending on the application you have, it is also possible that you run many threads in a high number so that the processor may hang because of priority management issues.

Rgrds

Posted: Sat May 15, 2004 10:03 am
by dmoc
Thanks for the info. So if the proc itself has terminated using KillThread() is safe?

Posted: Sat May 15, 2004 10:13 am
by fweil
... it should be !

I have noticed that it is not so sure.

When running an app using threads, if you use strings, which is a well known problem, I have had some head ache because the main program may end before threads are really terminated. I don't know how to make it really stable.

So, you have to register all threads you run, and manage your app so that you are sure what runs, and what ends, keeping the control in the main code.

I use to register by using either a linked list or an array, keeping the thread's ID until it ends. When a given thread ends, it cancels its instance in the array, so that the main code can know, what still runs or not.

And so on ...

Posted: Sat May 15, 2004 10:39 am
by dmoc
My threads avoid strings and only do the bare minimum required. Everything else is done in main code (I try hard to minimise headaches :P ).

I'm not being lazy here, of course I have tried various things but I have noticed that getting something to work doesn't mean that it will work over an extended period, eg, that 1/1000000 chance of a hick-up that causes your code to hang :? Anyway, thanks again.

Posted: Sat May 15, 2004 12:03 pm
by dmoc
Here's some code which shows my problem. No matter what I try the threads remain until the program (compiled on PB v3.81) actually quits. Any/all suggestions appreciated.

Code: Select all

; Monitored using: Process Explorer v8.20 (www.sysinternals.com)

Global n.l, q.l ; (n)umber of threads to run, (q)uit flag
Dim tid.l(10) ; Thread ID's
Dim tff.l(10) ; Thread Finished Flags

Procedure dumdedum(d.l)
  Shared q.l
  Delay(d): q=1 ; Delay then set quit flag
EndProcedure

Procedure athread(tn.l)
  Delay(2000+Random(3)*1000) ; 2..5 secs delay
  tff(tn)=1 ; Finished Flag
  ;KillThread(tid(n))
EndProcedure

; Start some threads
n=3 ; Number of threads to start
For i=1 To n: tid(i)=CreateThread(@athread(),i): Next

CreateThread(@dumdedum(),10000) ; WatchDog thread

; Monitor threads...
Repeat
  Delay(500)
  c=0
  For i=1 To n
    If tff(i)=1: c+1: EndIf
  Next
  If q: Debug "time-out": Break: EndIf
  If c=n: Debug "all-finished": Break: EndIf
ForEver

Debug "Still see the threads? 4 secs to check!"
Delay(4000) ; 4 sec while you check

; Now try explicit kill/ terminate...

For i=1 To n: If tid(i): KillThread(tid(i)): EndIf : Next
Debug "How about now? 4 secs to check!"
Delay(4000) ; 4 sec while you check

For i=1 To n: If tid(i): TerminateThread_(tid(i),0): EndIf : Next
Debug "Don't tell me they are still there!? 4 secs to check!"
Delay(4000) ; 4 sec while you check

End

Posted: Sat May 15, 2004 12:46 pm
by fweil
dmoc,

This is the way I would do :

Code: Select all

#NThreads = 10

Dim ThreadIDs.l(#NThreads)

Procedure athread(ThisThreadID.l)
  Delay(2000+Random(3)*1000)
  ThreadIDs(ThisThreadID) = 0
EndProcedure 

Procedure.l WatchThreads()
  Remains.l = 0
  For i.l = 1 To #NThreads
    If ThreadIDs(i) <> 0
        Remains + 1
    EndIf
  Next
  ProcedureReturn Remains
EndProcedure

  n= 10 ; Number of threads to start 
  For i=1 To #NThreads
    ThreadIDs(i)=CreateThread(@athread(),i)
  Next 

  Repeat
    Remaining.l = WatchThreads()
    Debug "@ " + FormatDate("%hh:%ii:%ss", Date()) + " remains " + Str(Remaining) + " threads"
    Delay(1000)
  Until Remaining = 0
    
End
Seems to work ... tell me about results with this code on your side.

Rgrds

Posted: Sat May 15, 2004 1:18 pm
by dmoc
Ah, maybe I haven't explained myself clearly: as far as PB code is concerned the threads have terminated, at least as far as you can tell from monitoring your own state vars, but using a process monitor utility (like SysInternal's "Process Explorer") the threads are definately still listed in the process table (or whatever). To see this add a delay to the end of the program to give you time to switch to the process monitor. If a program really is terminating then it's not a problem but my program is continually firing one-shot threads which when exited/ killed/ terminated *still* remain. So the thread count according to system resources just continues increasing! See my problem?

Posted: Sat May 15, 2004 1:39 pm
by fweil
I don't know really the way to ensure which is right and which is wrong.

By experience, I know that threads are really ended if the internal app monitoring tells so. And the allocated emory is freed also.

Using an external monitor does not give me more reults than what I see on the task manager.

When I did real tests, I was playing with usually 200- 1.000 threads for networking stuff, and I am sure whether threads release or not.

Well, ... can't tell more.

Posted: Sat May 15, 2004 4:44 pm
by dmoc
Thanks for your help anyway. I did a google and came up with the following page...

viewtopic.php?t=6146&view=next

...yeah, right back here (funny I couldn't see it when I searched the forums). Turns out that "CloseHandle_(tid(i))" is preferable to other commands. I tried it and it seemed to do the job. I say "seemed" because with my quick test one or two threads were left over. Tomorrow I'm going to test with more threads and and see how it performs.

Posted: Sat May 15, 2004 5:00 pm
by fweil
Well,

Give us feedback then ...

Rgrds

Posted: Sat May 15, 2004 5:02 pm
by dmoc
Silly me, forgot about the stuff detailed near the end of this MS page...

http://msdn.microsoft.com/library/defau ... hreads.asp

...although I did think PB handled this itself.

Posted: Sun May 16, 2004 5:06 am
by PolyVector
I do this sorta thing

Code: Select all

Global QuitEvent.l
QuitEvent=CreateEvent_(#Null,#True,#False,"MyQuitEvent")

Procedure ThreadProc(x)
  Repeat
    Debug x
  Until WaitForSingleObject_(QuitEvent,0)=0
  Debug "Exit>"+Str(x)
EndProcedure

Dim hThread(10)
For x=1 To 10
  hThread(x)=CreateThread(@ThreadProc(),x)
Next

Delay(1000)

SetEvent_(QuitEvent)

For x=1 To 10
  WaitThread(hThread(x))
Next

Debug "All Threads Finished..."
Or you could use my ThreadSync library... :D

Posted: Sun May 16, 2004 1:48 pm
by dmoc
PolyVector, I like your library but my only problem is getting rid of them once they are finished (with the prog still running, as it should be). I found the reason and it's because win98 still maintains an entry for a finished thread so it's exit code can be queried. Once all handles to a particular thread are freed then even this disappears. I don't think PB closes thread handles until the program itself exits.

fweil, update soon :D

Posted: Sun May 16, 2004 3:13 pm
by dmoc
I've tried it a number of times with a varying number of threads and CloseHandle_() seems to reliably purge thread leftovers. So I'm a happy chappy :D But of course one thing leads to another and I wonder now how you can tell if a thread is still alive without keeping your own flags. Seems a reasonable request so I looked for a win32 function and can't find one. The nearest I see is GetThreadTimes_() but the key bit of info "lpExitTime" (a FILETIME structure) is undefined if the thread has not exited :roll: You'd think it would at least be a null ptr. Not to worry though, it's not a problem for me.