Solved

URGENT: Problem with threads

Posted on 2004-10-25
626 Views
Last Modified: 2010-04-05
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...
0
Question by:Ivanov_G
    9 Comments
     
    LVL 6

    Expert Comment

    by:vadim_ti
    FreeOnTerminate only destroy thread, but will not assign nil  to thread variable,
    you can nil array element in onDestroy method of your thread
    0
     
    LVL 6

    Expert Comment

    by:vadim_ti
    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
     
    LVL 26

    Expert Comment

    by:Russell Libby

    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
     
    LVL 12

    Author Comment

    by:Ivanov_G
    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
     
    LVL 6

    Assisted Solution

    by:vadim_ti
    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
     
    LVL 26

    Accepted Solution

    by:
    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
     
    LVL 26

    Expert Comment

    by:Russell Libby
    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
     
    LVL 12

    Expert Comment

    by:Lee_Nover
    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
     
    LVL 12

    Author Comment

    by:Ivanov_G
    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

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    Cisco Complete Network Certification Training

    If you’re an IT engineer or technician, it's time you take your career to the next level. This elite training bundle is brimming with all of the information you need to learn to sit for Cisco CNNA, CCNP, and CCENT certification exams.

    This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
    Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
    Need more eyes on your posted question? Go ahead and follow the quick steps in this video to learn how to Request Attention to your question. *Log into your Experts Exchange account *Find the question you want to Request Attention for *Go to the e…
    Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

    884 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    15 Experts available now in Live!

    Get 1:1 Help Now