5,447,640 members and growing! (20,939 online)
Email Password   helpLost your password?
General Programming » Threads, Processes & IPC » Threads     Intermediate License: The Code Project Open License (CPOL)

Check-Point in Background Thread After Application Exit

By Bharath K A

This article is about a kind of worker background thread that wishes to live a little longer than the application exit. This gives the background thread an opportunity to check-point its closure. This helps the background thread to resume its operation from where it was terminated.
C# 1.0, C# 2.0, C#, Windows, .NET, .NET 1.0, .NET 1.1, .NET 2.0VS.NET2003, VS2005, Visual Studio, Dev

Posted: 28 Sep 2007
Updated: 17 Aug 2008
Views: 6,285
Bookmarked: 8 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
6 votes for this Article.
Popularity: 1.09 Rating: 1.40 out of 5
4 votes, 66.7%
1
2 votes, 33.3%
2
0 votes, 0.0%
3
0 votes, 0.0%
4
0 votes, 0.0%
5

Introduction

We all know about .NET background threads. Background threads are the kind of threads that don't vote for application exit. Consider an application that has a non-background thread and 20 background threads; then, upon exit of the non-background thread, the application exits (all the 20 background threads halt abruptly).

But, consider the following cases:

  1. You wish to check-point your background thread's progress before exiting the application. This may be useful to resume the operation from the check-point location.
  2. You wish to persist data just before the application is shut down by the user.
  3. You wish to log a few details just after application is shut down by the user.
  4. You wish to safely turn off a hardware item (such as a medical instrument), even if the user chooses to abruptly close without shutting down the hardware, etc..

In all the above mentioned cases, the background thread needs to live a little longer than the application exit. The design goal is to have the background thread at least reach a check-point where it can safely shut down after application exit.

Another factor to consider while designing the above mentioned background thread is: the application being restarted by the user anytime immediately after the closing.

This article is about the worker background thread that wishes to live a little longer than application exit. The aim of this article is to give a brief overview, while the real world solution requires modifications as described in the 'Points of interest' section.

Background

A real life example: consider I have an attractive download application. When the user chooses to close the application (not tray minimize, but terminate the application), I would like to store my partial downloads. But, being a user-friendly application, I would like to cut-off the User Interface (stop message pump) immediately. At this point, I have a requirement to have the background thread to live a little longer to do the persistence and cleanup work. Also, I risk the application being restarted by the user before the background thread finishes the job.

To give one more real life example: consider a medical application that needs to shutdown a connected instrument robot before application exit. The application has a specific UI element (such as a 'Shut Down' button) provided for this functionality. But, say, the user closes the application without shutting down the instrument. Now, such a background thread may be used to have the shutdown sequence executed. This can be done while the UI is removed off the scene immediately.

Also one more point worth mentioning here is – Most of the customers hate the application that takes longer time to exit showing dead inactive UI elements for few seconds before exit.

Using the code

Now, it is time for the design. The design goal is to have a class that provides the above mentioned functionality. Let's name this class NonExitBackgroundWorker. The two important methods exposed by this class are:

  • RegisterMainMethodAndBackgroundThread - This method is used to register the Main method. This is necessary because, if the user restarts the application before the background thread could finish its operations, then a listener thread will call the Main method to start the application again. This thread's main functionality is just to listen for any application start event. If the application is started by the user before the exit of the background thread, then this listener thread calls the application to start the Main method asynchronously.
  • StartApplication - This method should be called at the first line of the application. This function checks if the application's previously started instance is active (happens even if the application is closed and if the background thread is active). If active, then it informs the application to start the main delegate that was passed earlier. This function is intentionally implemented as empty.

The error and exception handling are avoided for brevity.

using System;
using System.Threading;

/// <summary>
/// Stealthy Background Thread
/// </summary>

public class NonExitBackgroundWorker
{

    public delegate void MainMethod();
    public delegate void ThreadStart();
    private static EventWaitHandle EventToStartApplication;
    private static bool m_bCreatedNew;
    private static bool m_bWaitForExit;
    private static MainMethod MainMethodDelegate;

    static NonExitBackgroundWorker()
    {
    
        EventToStartApplication = new EventWaitHandle(false, EventResetMode.AutoReset, 
               "MyEvent43997C2F-33A4-49e1-B5C4-7BC0FE6C6BCF", out m_bCreatedNew);
        m_bWaitForExit = false;
        if (!m_bCreatedNew)
        {
            EventToStartApplication.Set();
            Environment.Exit(0);
        }
    }

    public static void RegisterMainMethodAndBackgroundThread(MainMethod MainMethodFunc, 
                       ThreadStart ThreadStartFunction)
    {
        if (!m_bWaitForExit)
        {
            MainMethodDelegate = new MainMethod(MainMethodFunc);
            AsyncCallback theAsyncCallbackToCloseApp = 
                     new AsyncCallback(StopApplicationAndBKThread);
            ThreadStartFunction.BeginInvoke(theAsyncCallbackToCloseApp, null);
            System.Threading.Thread thApplicationStartListener = 
                   new System.Threading.Thread(ApplicationStartListener);
            m_bWaitForExit = true;
            thApplicationStartListener.Start();
        }
    }

    private static void ApplicationStartListener()
    {
        while (m_bWaitForExit)
        {
            if (EventToStartApplication.WaitOne() && m_bWaitForExit)
            {
                MainMethodDelegate.BeginInvoke(null, null);
            }
        }
    }

    public static void StopApplicationAndBKThread(IAsyncResult AsynchResult)
    {
        m_bWaitForExit = false;
        EventToStartApplication.Set();
    }

    public static void StartApplication() { }

}

Now, let us walk through the code:

The first method is the static constructor NonExitBackgroundWorker. Consider that the application is already running. As we discussed previously, you brace your code calling StartApplication in the first line of the Main method. Now, the static constructor gets called. It creates the unique event (with a unique GUID). The out variable m_bCreatedNew tells us if the application's background thread is still running. If the application's background thread is not yet finished, then we set the event. This is done to tell the instance of the running application that starts the main UI and the message pump. The next case to consider is: this is the first instance of the application started. Here, the event is created and the function exits. The if block is never executed.

The second method, which is also an important function, is RegisterMainMethodAndBackgroundThread. This method takes two parameters. The first parameter is the MainMethod delegate and the second one is the background thread delegate. It stores the Main method delegate in a variable, to call at a later stage. It calls the background thread asynchronously with a callback function. This callback function is necessary to terminate the application once the background thread finishes its task. Now, it creates a new thread to listen. The main 'if' block is to ensure that the thread is created only once even if this method is called accidentally many times.

The third method is the ApplicationStartListener to listen for another instance of the application starting while the background worker-thread is not yet complete. Now, look at the first method. If the event is not a newly created event, then it triggers the event. This means the first instance of the application which was listening will get triggered to call the Main method delegate. This delegate will invoke the application Main method, which is supposed to trigger the UI and the message pump.

The fourth method StopApplicationAndBKThread is called once the background thread completes its critical task. The background worker's callback function calls this method to terminate the application.

How to use the attached sample

The attached sample contains a simple C# application, with a UI as shown in the figure above. The background thread will be alive (doing its last minute critical task until you press the 'Shut down App…' button. You can try to close the UI by pressing the 'x' at the top right hand side. Try to restart the application. Now, you will see the same instance starting the UI, while the new instance goes away. This can be verified by looking at the PID in the Task Manager.

Points of interest

There are many points necessary to consider while using this class for commercial application development:

  1. It may be necessary to have many background threads to do some active task after the main GUI is closed. In this case, you need to modify the RegisterMainMethodAndBackgroundThread function to store the main method once. Or, have another method to register the main method, but this method just to have the background thread invoked.
  2. If it is necessary to enforce only one instance of the application to be running in a system, a new method EndApplication may be required to be implemented. This method will have a new boolean variable set while ending the application. This method shall be called at the last line of the application or in the application exit code.
  3. As in the sample, it may be easier to have two application entry methods, one to be called during regular startup, and the other to be called during instantiation by the background thread.
  4. An application may require getting a command line argument at startup. The class may require to store it and use it during restart.
  5. Not to forget, error and exception handling is required.
  6. The implementation of the background thread should periodically check if the application's main UI is closed. If closed, then the background thread should safely come to a completion state. If necessary, it may check-point its termination for future recovery.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Bharath K A



Occupation: Software Developer (Senior)
Location: United States United States

Other popular Threads, Processes & IPC articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
  (Refresh) 
Subject  Author Date 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 17 Aug 2008
Editor: Smitha Vijayan
Copyright 2007 by Bharath K A
Everything else Copyright © CodeProject, 1999-2008
Web20 | Advertise on the Code Project