CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3
  1. #1
    Join Date
    May 2005
    Posts
    6

    Problem with stopping Sub-Thread

    In my program, the user can start a calculation, that can take, cause of its complexity, several minutes.
    While the algoritm works, the user sees a progress-bar in a modal dialog. This dialog has also a Cancel-Button.
    The Calculation-Routine is started from this dialog-control-class as a thread, with _beginthread. If the user pushs the Cancel-button, the thread is killed by CloseHandle-Statement.

    In the function who was started as a thread, another function is called, that makes the calculation recursively. This takes, see Taskmanager, during the calculation, about 99% of CPU-performance.
    If the Cancel-Button in the progress-bar-dialog has been clicked, then the dialog-control is stopped, but the CPU-usage is still at 99%. That means that in the background the calculation continues, although this should have stopped.
    Until now I thought, that by killing a thread wiht CloseHandle, also all functions would be stopped automatically, that were called by the thread-function. Certainly it is not like I thought. As you can see, I am not an expert in programming multithreading.

    So please tell me: What is the trick that is fullfilling the goal, that by killing the dialog control (progress-bar-dialog), also the functions will be stopped in their batch, that were called from this dialog-control-class ?
    Until now, this continuing process (calculation, that still takes 99% CPU after the dialog-thread had been stopped) is stopped not until the whole program ends. That's not okay and has to be solved.

    Same text in German language:

    In meinem Programm kann der User eine Berechnung starten, die wegen ihrer Komplexität durchaus auch mal mehrere Minuten dauern kann.
    Dem User wird in der Zeit in einem modalen Dialog ein Fortschrittsbalken angezeigt. Dieser Dialog enthält auch einen Abbrechen-Button.
    Die Berechnungs-Routine wird aus der Dialogsteuerungsklasse heraus als Thread gestartet, mit _beginthread. Wenn der User den Abbrechen-Button klickt, so wird der Thread gekillt, mit CloseHandle(<threadhandle>).

    In der Funktion, die als Thread gestartet wird, wird eine andere Funktion aufgerufen, die dann rekursiv die Berechnung durchführt. Das nimmt im Taskmanager für die Dauer der Berechnung dann 99% der CPU-Leistung ein.
    Wenn der Abbruch-Button in dem Fortschritts-Dialog geklickt wird, dann endet zwar die Dialogsteuerung, jedoch bleibt die CPU-Auslastung bei 99%. Das heißt dass im Hintergrund die Berechnung "im geheimen" weiter läuft, obwohl diese gestoppt werden sollte.
    Ich dachte bisher, dass wenn ein Thread mit CloseHandle gestoppt wird, dadurch auch alle Aufrufe die aus diesem Thread heraus erfolgt sind, als Prozess gestoppt werden. Das ist aber ganz offensichtlich nicht so. Wie man sieht, bin ich kein Experte in der Multithread-Programmierung. Bisher ist dieses Gebiet für mich eher noch recht undurchsichtig.

    Wo liegt also bitte der Trick, der dazu führt, dass mit dem Killen der Dialog-Steuerung (Fortschritts-Balken-Dialog) auch die aus diesem Dialog heraus aufgerufenen Funktionen ihre Abarbeitung auch sofort einstellen ?

    Bisher ist es so, dass dieser "geheime" weiterlaufende Prozess (der wie gesagt sich dann weiterhin 99% CPU heraus nimmt) erst mit der Beendigung des gesamten Programms auch aufhört. Das darf nicht sein.

    Code of thread-control-class
    Code:
    AsyncTask::AsyncTask ()
    {
       m_hThread = NULL;
       m_hCancelEvent = NULL;
    } // Konstruktor
    
    
    AsyncTask::~AsyncTask ()
    {
       if (NULL != m_hCancelEvent) 
       {
          ::CloseHandle( m_hCancelEvent );
          m_hCancelEvent = NULL;
       }
       if (NULL != m_hThread) 
       {
          ::CloseHandle( m_hThread );
          m_hThread = NULL;
       }
    } // Destruktor
    
    
    unsigned int __stdcall
    AsyncTask::ThreadLoader( void* pArgs )
    {
       AsyncTask* pSelf = (AsyncTask*)pArgs;
       pSelf->m_iExitCode = pSelf->action();
       return pSelf->m_iExitCode;
    } // ThreadLoader
    
    
    BOOL AsyncTask::Start ()
    {
       if (!Started())
       {
          m_hCancelEvent = ::CreateEvent(NULL,FALSE,FALSE,NULL);
          if (NULL == m_hCancelEvent)
          {
             return false;
          }
    
          unsigned int iThreadId;
          m_hThread = (HANDLE)::_beginthreadex(
             NULL, 0, AsyncTask::ThreadLoader, this, CREATE_SUSPENDED, &iThreadId 
          );
          ::ResumeThread(m_hThread);
       } // if
       return NULL != m_hThread;
    } // Start
    
    
    BOOL AsyncTask::Cancel (unsigned int timeout_ms)
    {
       if (!Started())
       {
          return true;
       }
    
       DWORD dwResult = ::SetEvent(m_hCancelEvent);
       dwResult = ::WaitForSingleObject(m_hThread,timeout_ms);
       if (WAIT_OBJECT_0 == dwResult)
       {
          ::CloseHandle(m_hThread);
          m_hThread = NULL;
          return true;
       } // if
       return false;
    } // Cancel
    
    
    void AsyncTask::Kill ()
    {
       if (!Started())
       {
          return;
       }
       ::CloseHandle(m_hThread);
       m_hThread = NULL;
    } // Kill
    
    
    BOOL AsyncTask::Started () const
    {
       return NULL != m_hThread;
    } // Started
    
    
    BOOL AsyncTask::CancelRequested () const
    {
       return WAIT_OBJECT_0 == ::WaitForSingleObject(m_hCancelEvent, 0);
    } // CancelRequested
    Code-part of dialog-control-class:
    Code:
    class DIALOG_CONTROL_CLASS_PROGRESSBAR : public CDialog, public Calculation, public AsyncTask
    {
       private:
    ...
          unsigned int action();
    ...
       public:
          DIALOG_CONTROL_CLASS_PROGRESSBAR ();
    }
    
    Implementation:
    
    
    unsigned int DIALOG_CONTROL_CLASS_PROGRESSBAR::action ()
    {
    ...
    
                CalculationComplex (tempPackNumber,
                          length, width, height,
                          tempLengthResult, tempHeightResult, tempWidthResult,
                          tempPosResultX, tempPosResultY, tempPosResultZ,
                          tempIsLoaded,
                          &index
                         );
    
    ...
    }
    So the problem is, that CalculationComplex is still working, after the action()-Thread has been killed.

  2. #2
    Join Date
    Feb 2002
    Posts
    4,640

    Re: Problem with stopping Sub-Thread

    "CloseHandle" does not kill threads, it just releases the handle you have back to the system. The opposite is not true, in that a thread will not completely finish, if there are open handles to the thread.

    You need to add some kind of messaging. When you click cancel, set some "kill" variable. Then, in your processing, periodically check that variable. If it's true, stop processing and exit the thread function.

    Viggy

  3. #3
    Join Date
    Jan 2010
    Posts
    12

    Re: Problem with stopping Sub-Thread

    try to do your calculation in a step-by-step fashion (in a way that can be done with a while loop or a for loop) and to signal cancellation, insert a WaitForSingleObject with a timeout of 1ms or so, that will give the UI chance to jump in and update the progress bar, as well as respond to user interface.

    Before entering the thread, create a handle to an event (CreateEvent) and pass the handle to the thread (in a struct/class pointer perhaps?).

    To cancel the thread, set the event and you can trap it (result WAIT_OBJECT_0 from WaitForSingleObject = exit loop) then your thread should exit it's loop. As the thread is in a loop (instead of a recursive function) you won't stack overflow, and you won't have to wait for the function calls to unravel (which if you've gone a long way up the stack, can take a little while)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured