URGENT: Problem with threads

I have input file - plain text in the format:
1
2
3
...

I use this file for open URL, submit the value as a parameter, retrieve some info and save it to file. I use a dynamic array of TMyThread type. Let's say I set its length to 30. Then loop through Low() and High() and check if the element is nil - create the thread. If it is not - check if it is running (public var in the thread). I set FreeOnTerminate = True, but somehow the thread is not nil after it finished.

I the loop if the thread has finished (it should be nil because FreeOnTerminate = True) I create a new instance. But Somehow the finished thread is not nil... I create it with CreateSuspended = False...
LVL 12
Ivanov_GAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

vadim_tiCommented:
FreeOnTerminate only destroy thread, but will not assign nil  to thread variable,
you can nil array element in onDestroy method of your thread
0
vadim_tiCommented:
think abot it, you can save Thread object in number of different variables, thread could not to nil them (and even to know
about them)
but in your on destory event you can loop through your array
for (....) {
  if (ThreadArr[i] = Self) then begin
     ThreadArr[i] := nil;
     break;
  end
0
Russell LibbySoftware Engineer, Advisory Commented:

It would actually be better to set the OnTerminate method handler for the thread, as this will run in the context of the main thread.

OnTerminate
---------------
Occurs after the thread's Execute method has returned and before the thread is destroyed.

property OnTerminate: TNotifyEvent;

Description

Write an OnTerminate event handler to execute code after the thread finishes executing. The OnTerminate event handler is called in the context of the main VCL thread, which means VCL methods and properties can be called freely. The thread object may also be freed within the event handler.


---

Regards,
Russell
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

Ivanov_GAuthor Commented:
I think I have to clarify few things

type
   ThreadArr     : array of TMyThread;

    for counter := Low(ThreadArr) to High(ThreadArr) do
      begin
        if ThreadArr[counter] = nil then
          begin
            if InputStrings.Count > 0 then
              begin
                ThreadArr[counter] := TMyThread.Create(False, ThreadOptions, InputStrings[0]);
                InputStrings.Delete(0);
              end;
          end
        else
          if ThreadArr[counter].IsFinished then
            begin
              ThreadArr[counter].Terminate;
              ThreadArr[counter] := nil;
            end;
      end;

This is the case here... Is finished is public variable in the thread which shows me if everything with the HTTP ended correctly...
0
vadim_tiCommented:
if from any reason your execute method will be finished before you will check
   ThreadArr[counter].IsFinished
you are in troubles.

i think safer place for nilling ThreadArr[counter] is in thread destructor or in finally part of thread execute method
(if you can be sure never exceprion will be raised in Thread constructor)

on the safe side you can protect ThreadArr via critical section.
0
Russell LibbySoftware Engineer, Advisory Commented:
I agree with part of the above statement.

>> if from any reason your execute method will be finished before you will check
   ThreadArr[counter].IsFinished
you are in troubles.


The IsFinished is a public variable of the thread, but the thread is set to "free on terminate", so if it frees itself when done, then the context of this variable is no longer valid.

---

I would not try to access the main thread from the thread destructor though, but would use the OnTerminate event to handle this:

eg:

type
  ThreadArr     : array of TMyThread;

  for counter := Low(ThreadArr) to High(ThreadArr) do
  begin
     if (ThreadArr[counter] = nil) then
     begin
        if InputStrings.Count > 0 then
        begin
           try
              ThreadArr[counter]:=TMyThread.Create(False, ThreadOptions, InputStrings[0]);
              InputStrings.Delete(0);
           finally
              ThreadArr[counter].OnTerminate:=OnMyThreadTerminate;
           end;
        end;
     end
     else
        // Terminate the thread (it will clean itself up
        ThreadArr[counter].Terminate;
  end;

  // Replace TForm1 with whatever your main object class is
  TForm1.OnMyThreadTerminate(Sender: TObject);
  begin
     // Walk the list
     for counter:=Low(ThreadArr) to High(ThreadArr) do
     begin
        // Check indexed thread with sender
        if (ThreadArr[counter] = Sender) then
        begin
           // Found item, nil out and break loop
           ThreadArr[counter]:=nil;
           break;
        end;
     end;
  end;

--

This will allow you to clear the thread list from within the main thread, which is where it **should** be done. If you wish to access threadarr variable from the Thread's destructor then the use of a mutex/critical section would be MANDATORY.

One side note to this. The execute method of the thread should be written to periodically check the Terminated property. From the help file:

Indicates whether the thread has been asked to terminate.

property Terminated: Boolean;

Description

The thread's Execute method and any methods that Execute calls should check Terminated periodically and exit when it's True.. The Terminate method sets the Terminated property to True.

The Terminate method is the polite way to abort the execution of a thread, but it requires cooperation from the thread’s Execute code. Using terminate is recommended over the TerminateThread Win32 API call.

eg:

procedure TMyThread.Execute;
var finished: Boolean;
begin

 while not(Finished) and not(Terminated) do
 begin
       ....
      // Set finished when finished
 end;

end;

or

procedure TMyThread.Execute;
begin



end;


------------

Russell
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Russell LibbySoftware Engineer, Advisory Commented:
Oops, posted too quick...

another example of execute without looping

procedure TMyThread.Execute;
begin

  // perform first step
  ....

  if not(Terminated) then
  begin
     //  next step
     ....
     if not(Terminated) then
     begin
        // next step
        ....
        etc....
     end;
  end;

end;

----
Regards,
Russell
0
Lee_NoverCommented:
weird pooling scheme if it is ment to be one
for a thread pool check for example Indy's ThreadPool
the threads that finish just get suspended .. when needed nonworking threads get returned, if no thread is free a new one is created
think of something in the lines of:
while InputStrings.Count > 0 do
begin
  thd:=ThreadPool.GetAvailableThread(true {allow create});
  thd.ProcessURL(InputStrings[InputStrings.Count-1]);
  InputStrings.Delete(InputStrings.Count-1);
end;

with proper locking you could make this 'live' :)
0
Ivanov_GAuthor Commented:
somehow ... some of the thread fails. If I run it once, it can finish successfully, but the second time, some thread will fail and this will stop the whole application...

I am trying to debug this now, but with so many threads ... grrr
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.