Thread: Closing threads

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Closing threads

    Thanks for all who helped with my last threads question. The CPU usage is now down to where it should be.

    Next problem is terminating the threads. My system thus far works, but it is slow.

    Each thread continues the loop, sleep, update, loop system as long as m_bActive is true and m_bVisible is true.

    To terminate the thread I added an m_bThreadTerminated boolean and when the CAnimSequenceContainer destructor is called it iterates through all of the animation sequences and sets the m_bActive member to false. Then it loops until m_bThreadTerminated is true. m_bThreadTerminated is set to true when the thread loop exits due to m_bActive being false. It works and there are no leaks, but shutdown is a bit slow.

    Is there any ways to exit quicker using threads while ensuring they shut down properly prior to the main process exiting?

    Inside of CAnimSequence
    Code:
    static UINT DoThreading(LPVOID p)
        {
          CAnimSequence *ptrSeq=(CAnimSequence *)p;
          
          while (ptrSeq->m_bActive)
          {
              
            if (ptrSeq->m_pWindow!=NULL)
            {
    
              if (ptrSeq->m_bVisible)
              {
            
                ptrSeq->m_fCurTime=(float)::timeGetTime();
                float fElapsed=ptrSeq->m_fCurTime-ptrSeq->m_fLastTime;
           
                 float fPause=ptrSeq->GetCurFrameDuration();
                 ::Sleep(ptrSeq->GetCurFrameDuration()*.10f);
                 ptrSeq->Update(fElapsed);
              
                ptrSeq->m_fLastTime=ptrSeq->m_fCurTime;
              }
           
            }
          }
    
          ptrSeq->m_bThreadTerminated=true;
    
          return (0);              
        }
    Inside of CAnimSequenceContainer
    Code:
    virtual ~CAnimSequenceContainer(void)
      {
        //Terminate threads
        for (DWORD i=0;i<AnimSeqs.size();i++)
        {
          AnimSeqs[i]->m_bActive=false;
          while (AnimSeqs[i]->m_bThreadTerminated==false)
          {
            //Wait for thread to terminate
          }
          if (AnimSeqs[i])
          {
            delete AnimSeqs[i];
          }
    
        }
    
    
        AnimSeqs.clear();
    
      }
    Last edited by VirtualAce; 12-30-2005 at 02:59 AM.

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    847
    perhaps You could use a reference counter and have each thread decrement it once it exits. That way your loop which sets m_bActive to false in CAnimSequenceContainer dosen't have to wait on each thread before singaling the next to exit. You need only call one wait loop after you set all of the m_bActive to false which waits for the reference counter to reach 0. I beleive this would give a speed increase as serveral threads will exit at around the same time.

  3. #3
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Then it loops until m_bThreadTerminated is true
    Can you change the code to something like the following? This way you don't have to poll a boolean variable which takes time and cycles.

    Code:
    virtual ~CAnimSequenceContainer(void)
    {
        HANDLE  threadHandles = new HANDLE[AnimSeqs.size()];
        //Terminate threads
        for (DWORD i=0;i<AnimSeqs.size();i++)
        {
            AnimSeqs[i]->m_bActive=false;
            threadHandles[i] = AnimSeqs[i]->m_threadHandle; // hopefully you are storing the thread handle here
        }
        WaitForMultipleObjects(AnimSeqs.size(), threadHandles, TRUE, INFINITE);
        delete [] threadHandles;
    
         for (DWORD i=0;i<AnimSeqs.size();i++)
        {
            if (AnimSeqs[i])
            {
                delete AnimSeqs[i];
            }
        }
    
        AnimSeqs.clear();
    
    }

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I like it. Will try.

    Thanks.

    And in case you are wondering I have to check for the validity of AnimSeqs[i] because MSVC 6 has a bug in which you cannot call delete on a NULL pointer even though the standard allows this.

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I get these errors when trying that code:

    AnimToolDlg.cpp
    D:\MSVC 6 Projects\ZeldaEditor\CAnimSystem.h(307) : error C2036: 'void *' : unknown size
    D:\MSVC 6 Projects\ZeldaEditor\CAnimSystem.h(307) : error C2120: 'void' illegal with all types
    D:\MSVC 6 Projects\ZeldaEditor\CAnimSystem.h(310) : error C2664: 'WaitForMultipleObjects' : cannot convert parameter 2 from 'void *' to 'void *const * '
    Conversion from 'void*' to pointer to non-'void' requires an explicit cast
    What data type is HANDLE? Just a DWORD or UINT?

    Fixed.

    HANDLES *threadHandles=new HANDLE[AnimSeqs.size()];

    I moved the waiting to the CAnimFrame destructor and used WaitForSingleObject(m_pThread->m_hThread,INFINITE);

    It works great. Thanks for the idea.
    Last edited by VirtualAce; 01-02-2006 at 10:25 AM.

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    HANDLE is void*.

    Is it intentional that bVisible being false or the window pointer being NULL causes the thread to go into an unrestricted infinite loop? Looks like a CPU eater to me.
    Also, I don't think the thread-per-animation model is a good one. A thread is lighter than a process, but I still think it's too heavy for each of your animations having its own. The context switches are especially heavy.
    I think AnimSequenceContainer should create exactly one thread that handles all animations, like this:
    Code:
    ThreadProc
    {
      loop {
        animation = find next animation that needs a step advance
        timeout = animation.getTimeOfNextFrame() - now;
        if(timeout > 0) {
          ret = WaitForSingleObject(ThreadTerminatorEvent, timeout);
          if(ret == timed out) break loop;
        }
        animation.update(elapsed time);
      } forever;
    }
    Then you could wait for that single thread to terminate instead of multiple. To make the thread terminate, you activate the kill event. (Look up CreateEvent if you're unfamiliar with events.) Making this an event of a boolean eliminates the delay of waiting until the next animation step is performed.


    Another option would be a higher-level library like the experimental Boost.Asio (currently in review, but I'm confident its first release will be in Boost 1.34). This library can schedule function objects to execute after a timer runs out. You can schedule any amount of timeouts, and they all run within the same thread.
    Last edited by CornedBee; 01-02-2006 at 10:15 AM.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Well I've done this both ways. I started out with one thread and it didn't do very well. When I offloaded that to other threads it has done great.

    The animation is really not much in itself. My whole system is based on ID's, so all it does is count to FrameCount, increment frame index, draw, and repeat. All of the Tile device contexts or DC's are pre-prepared and have the bitmap already on them. No SelectObject() or anything of the like is required. Just blit the Tile DC to the source DC and you are done. I've checked the process viewer and most of the threads are taking 20 to 30% CPU time. I can lower that by sleeping more but I don't want to miss frame changes so I can't sleep forever. Also, the code only draws when it knows the frame has changed since there is no reason to draw it if it hasn't.

    So it's basically a lot of counters running. Not too much but I do understand what you are saying.

    The m_bVisible is to indicate if the window is visible or not. In the editor when you X out a dialog window or tool window it will simply hide it. I don't want to update the animations in the dialog or any other window if it's not currently visible. The window being NULL was from previous code and will be removed. I wasn't sure how to check to see if the thread was still 'running' or not.

    I still have to place some semaphores on the Tile DC's since the tile palette window and the animations do conflict at times causing flickering in the tile palette. This is cause by two threads asking for the same DC at the same time.
    Last edited by VirtualAce; 01-02-2006 at 10:33 AM.

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    The less work the animation thread does, the less it is justified to have a thread all for its own.

    Of course, I don't quite see why you have animations in a separate thread in the first place. Don't you have a main frame advancing thread that could take this burden? The fewer context switches, the better.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The original reason I did it this way is because MFC provides no way to perform continuous updating. Now since then I've found code that does just that (by cutting and pasting code from the CDialog::OnIdle() to my OnIdle()) but I haven't tried it yet.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 5
    Last Post: 10-17-2008, 11:28 AM
  2. Closing child threads
    By nickname_changed in forum Windows Programming
    Replies: 4
    Last Post: 09-28-2003, 12:32 AM
  3. Premature closing of threads
    By *ClownPimp* in forum A Brief History of Cprogramming.com
    Replies: 4
    Last Post: 03-07-2003, 11:27 AM
  4. Block and wake up certain threads
    By Spark in forum C Programming
    Replies: 9
    Last Post: 06-01-2002, 03:39 AM