Safely Stopping Threads

Please don't kill your thread...

.

Environment: Win32, VC++ 5.0-6.0, Win 2000, NT 4.0, Win95/98

Preface

Some days ago I read the article "A Generic C++ Thread Class" published by Arun N Kumar on the codeguru Web site. I was curious about his ideas because some years ago I also wrote a little C++ thread class. Reading the article and looking at the code I found some good ideas but I also had some doubts and decided to give a comment about this article. I soon realized that I had too much to say, so I decided to write anarticle. I searched for my code, did some cleanup, and started writing. I hope you enjoy it!

My solution...

The first problem you encounter if you think about a thread class is the fact that all API functions used to create a thread want to have a pointer to a function. So passing a pointer to a member function is not possible cause a member function has always an implicit "this" pointer in their function signature. Therefore you have to use the following workaround: Add a static function to your class, pass the address of this static function to the API. The static function itself then has to call a member function which contains the code to execute the thread. To start a thread I choosed the API _beginthreadex() cause this method initializes certain C run-time library variables. This is important only if you use the C run-time library in your threads.

If the start of a thread works you will encounter another problem: "how can I stop it ?". I have seen a lot of examples where programmers are using the API TerminateThread() to stop the thread execution. This is not a good idea! Why? TerminateThread() is like killing your thread! The thread doesn't get the chance to cleanup any used resources (for example file handles, network sockets). To be detailed I copied the following statements from the MSDN Library:

TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code and its initial stack is not deallocated. DLLs attached to the thread are not notified that the thread is terminating.

TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:

  • If the target thread owns a critical section, the critical section will not be released.
  • If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
  • If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.

So for my understanding, TerminateThread() is not a good choice for a generic thread class.

My solution to stop a thread is more polite. My Thread class owns an event-object. An event-object works somehow like a traffic light it can be signaled or not. If I want to stop my running thread I trigger the event to be signaled. This is done in the Stop() method of my thread class.

Setting an event to signaled state is one half of the job. The second is that the thread-function has to check this event-object from time. If the event is signaled the thread stops execution of the thread code, do necessary clean-ups and return giving the thread exit-code.

To check an event-object you use the function WaitForSingleObject() in the window-system. This call is wrapped in the method AThread::isStopEventSignaled(). Usually a worker thread has a loop which executes the thread code for some time. So a good place to check the event-object is the loop-condition.

The class

This is the layout of my class AThread (the A stand for "another"):

class AThread
{
public:
  AThread();
  virtual ~AThread();
  bool Start();
  bool isRunning(DWORD dwTimeOut = 50) const;
  bool Stop();
  DWORD getThreadID() const;

protected:
  virtual unsigned ThreadFunction() = 0;
  static unsigned __stdcall ThreadStarter(void* PtrToInstance);
  bool isStopEventSignaled(DWORD dwTimeOut = 10) const;

//attributes
protected:
  unsigned m_ThreadID; //holds the threadID
  HANDLE m_hThread;    //holds the HANDLE of the created thread
  HANDLE m_hStopEvent; // holds the handle of the event-object
                       // to signal the thread that he has to stop
};

The described workaround with the static function is represented by the methods ThreadStarter() and ThreadFunction(). The address of ThreadStarter() is passed to _beginthreadex(). The implementation of ThreadStarter() just delegates the call to method ThreadFunction(), which contains the thread code. Please notice that ThreadFunction() is abstract you have to provide an implementation in a derived class.

The methods

Method name

Description

Start()

call this method to start thread execution

Stop()

call this method to stop thread execution. This method triggers the event-object m_hStopEvent.

isRunning()

gives information if the thread is currently running or not

getThreadID()

returns the thread identifier of a running thread

IsStopEventSignaled()

Checks the stop-event using WaitForSingleObject() if it's signaled or not. Returns false if the event is signaled. Makes implementation of ThreadFunction() a little bit easier

getExitCode()

call this method to get the exit code of the thread. If the thread is running, the value STILL_ACTIVE will be returned

Example:

I created the class ExampleThread to show how this works (see zip-file). This looks like:

class ExampleThread : public AThread
{
public:
  ExampleThread();
  virtual ~ExampleThread();

  //overwritten abstract method
  unsigned ThreadFunction();
};

the overwritten method ThreadFunction() contains the thread-code which is quite simple:

unsigned ExampleThread::ThreadFunction()
{
  //while loop will execute till the stop-event is signaled
  while(isStopEventSignaled() == false) {
    printf("<%i>", m_ThreadID);
  }

  //here is the place to cleanup/free resources
  printf("ExampleThread::ThreadFunction() after while loop\n");

  return 0; //return exit-code of thread
}

the while loop executes until isStopEventSignaled() returns false (the event-object is signaled). The code inside the loop just prints out the thread-id to the console window. Place code needed for cleanup after the while-loop().

The usage of the class looks like this (see main() function in the zip-archive):

ExampleThread myThread;
myThread.Start();
// The thread-code inside ExampleThread:: ThreadFunction()
// is now running
...
DWORD ID = myThread.getThreadID();
...
bool bIsRunning = myThread.isRunning();  //will return "true"
...
myThread.Stop();
...
bool bIsRunning = myThread.isRunning(); //will return "false" because
                                        //the thread is stopped
...
DWORD dwExitCode = myThread.getExitCode(); //get the exit code
                                           //of the thread

I have provided another example in the zip-archive which makes more fun (see class BounceCharThread). The code inside BounceCharThread::ThreadFunction() moves around a character inside the console window (this code was copied from a MSDN thread example). If a character reaches the border of the window, you will hear a beep. The main() function in the project HL_Thread (see zip-archive) creates 10 instances of the class BounceCharThread and call for each instance the Start() method. Therefore you will see 10 characters moving around in the console window. Have fun !

Conclusion

In this article I introduced a thread class that uses a polite (and safe) way to stop the execution of a thread. I hope I will see less code in the future using the API TerminateThread()!

As usual, if you have any doubts, questions, ideas, bugs please make a comment.
Thank you !

Downloads

Download demo project - 34 Kb


Comments

  • Anything similar to SIGTERM in UNIX world

    Posted by Legacy on 03/01/2002 12:00am

    Originally posted by: Paul

    What if the work thread call some function of a third-party COM which take a long long time or even not response.

    Then you need to terminate the thread, if you terminate by calling TerminateThread, the resource allocted by the third party COM is not released and most of the time will cause the program to crash instead of shut down.

    But there is no loop in the work thread? How do I stop the thread gracefully ?

    Reply
  • why using Abstract ThreadFuntion to start Worker Thread? Never FEEL that's too tedious?

    Posted by Legacy on 11/28/2001 12:00am

    Originally posted by: gamer wen

    //original
    class AThread
    {
    public:
    AThread();
    virtual ~AThread();
    bool Start();
    bool isRunning(DWORD dwTimeOut = 50) const;
    bool Stop();
    DWORD getThreadID() const;

    protected:
    virtual unsigned ThreadFunction() = 0;
    static unsigned __stdcall ThreadStarter(void* PtrToInstance);
    bool isStopEventSignaled(DWORD dwTimeOut = 10) const;

    //attributes
    protected:
    unsigned m_ThreadID; //holds the threadID
    HANDLE m_hThread; //holds the HANDLE of the created thread
    HANDLE m_hStopEvent; // holds the handle of the event-object
    // to signal the thread that he has to stop
    };

    //rewrite
    class AThread
    {
    public:
    AThread();
    virtual ~AThread();
    bool Start();
    bool isRunning(DWORD dwTimeOut = 50) const;
    bool Stop();
    DWORD getThreadID() const;

    /**********************************************************/
    void SetThrdFunct(pProcName);//added by gamer wen.
    //need me to give more about the implementation?hah.....
    /**********************************************************/

    protected:
    //virtual unsigned ThreadFunction() = 0;

    /**********************************************************/
    AFX_THREADPROC m_pProcName;//added by gamer wen.
    /**********************************************************/
    static unsigned __stdcall ThreadStarter(void* PtrToInstance);
    bool isStopEventSignaled(DWORD dwTimeOut = 10) const;

    //attributes
    protected:
    unsigned m_ThreadID; //holds the threadID
    HANDLE m_hThread; //holds the HANDLE of the created thread
    HANDLE m_hStopEvent; // holds the handle of the event-object
    // to signal the thread that he has to stop
    };


    Remember:
    Programme is an art, but to enjoy, not to bare. And to be honest, a WORKER THREAD is too simple outward to implement such a tedious (SURELY OO and inherent) work.

    GOOD LUCK!

    Reply
  • Good, but what about synchronising your Start() function

    Posted by Legacy on 11/28/2001 12:00am

    Originally posted by: Neil Strong

    You should really use a m_hStart event to synchronise startup with the calling process too, so, for example, your start function would do a:

    m_hStart = CreateEvent()
    _beginthreadex()
    WaitForSingleObject ( m_hStart )
    CloseHandle ( m_hStart ) ;
    .
    .
    .

    Your Thread function would then signal StartupEvent to tell the starting process the thread was started.

    It prevents race conditions if the main process then needs to communicate with the thread.

    Reply
  • Modifications for windows message handling

    Posted by Legacy on 11/27/2001 12:00am

    Originally posted by: Ian

    My worker thread communicates with the UI thread through
    
    WM_COPYDATA messages. (The UI thread uses these to update a
    results listbox with status messages).

    Without modification, its possible with AThread to end up
    deadlocked: the UI is waiting for the worker to stop, the
    worker is waiting for the UI to process the WM_COPYDATA
    message.

    The solution was alluded to by Alex Farber. Here is the
    modified Stop method and another method called
    WaitTillThreadHasTerminated() that will get around this
    problem.

    It also addresses AnJingBin's and Anan Shine's concerns
    about waiting infinitely for the worker. My stop() method
    now takes an optional timeout parameter. If things really
    have frozen up, it bites the bullet and calls
    TerminateThread(). Sorry, Heiko, sometimes you just have no
    choice!

    Anyway, here is the code:

    /*
    Stop()

    purpose: call this method to stop the thread execution

    param: none

    return value: "true", if the thread could be stopped, else "false"
    */
    bool AThread::Stop(DWORD dwMillisecondTimeOut /* = 5000*/)
    {
    //first check if the thread is running
    if (isRunning() == false)
    return true;

    if (m_hStopEvent != NULL) {
    //trigger the event to signal the thread to come to an end
    if (::SetEvent(m_hStopEvent) == 0) {
    return false;
    }
    ::Sleep(0); //give control to other threads
    if (!WaitTillThreadHasTerminated( dwMillisecondTimeOut ))
    {
    // Be brutal
    ::TerminateThread(m_hThread, -1);
    }
    m_ThreadID = -1;
    }
    else return false; //m_hStopEvent == NULL -> ERROR

    return true;
    }


    /*
    WaitTillThreadHasTerminated()

    purpose: this method will wait for the thread to terminate but will
    continue to pump messages, so that if Stop is call from a UI
    thread, the UI will not be blocked

    param: none

    return value: "true", if the thread has stopped, else "false"
    */
    bool AThread::WaitTillThreadHasTerminated(DWORD dwMillisecondTimeout)
    {
    while (TRUE)
    {
    // block-local variable
    DWORD result ;
    MSG msg ;

    // Read all of the messages in this next loop,
    // removing each message as we read it.
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
    // If it's a quit message, we're out of here.
    if (msg.message == WM_QUIT)
    return false;
    // Otherwise, dispatch the message.
    DispatchMessage(&msg);
    }

    // Wait for any message sent or posted to this queue
    // or for one of the passed handles be set to signaled.
    result = MsgWaitForMultipleObjects( 1,
    &m_hThread,
    FALSE,
    dwMillisecondTimeout,
    QS_ALLINPUT);

    // The result tells us the type of event we have.
    if (result == (WAIT_OBJECT_0 + 1))
    {
    // New messages have arrived.
    // Continue to the top of the always while loop to
    // dispatch them and resume waiting.
    continue;
    }
    else
    {
    return (result == WAIT_TIMEOUT);
    }
    }
    }

    Reply
  • A similar relevant problem...

    Posted by Legacy on 11/27/2001 12:00am

    Originally posted by: AnJingbin

    Same idea with Anna Shine,but if the worker thread blocked on a socket call,how to make it quit gracefully and as quick as possible. Is there any good solution to such kind of problems?

    Reply
  • So good,but...

    Posted by Legacy on 11/27/2001 12:00am

    Originally posted by: Anan Shine

    The class is good,but it has the fault that it cannot response quickly.You have to check the event once a loop-time.If the loop lasts for a long time,the user may be puzzled to see a clumsy thread.What do you think about that?

    Reply
  • Visual C++6.0

    Posted by Legacy on 11/26/2001 12:00am

    Originally posted by: idt

    Please send to me the soure code example Visual C++6.0.
    Thanks.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • The hard facts on SaaS adoption in over 80,000 enterprises: Public vs. private companies Mid-market vs. large enterprise GoogleApps, Office365, Salesforce & more Why security is a growing concern Fill out the form to download the full cloud adoption report.

Most Popular Programming Stories

More for Developers

RSS Feeds