September 7 2010




 
Search Blog Entries:



What is this?

Article Details
 
Creating multithreaded applications using the .NET Compact Framework - Part 1

Introduction

This article describes how to create multithreaded applications for the .NET Compact Framework. Using a very simple example we introduce a multithreaded application, and extend it step by step to introduce you to the fundamentals of multithreaded applications. Before introducing the example we will spend a few words on the difference between processes and threads inside the Windows CE Operating System.

Windows CE Processes

In the Windows CE Operating System, as in any other Windows Operating System, a process is a placeholder for an application. It is an instance of a running application. A process in Windows CE does not execute any code. Instead, a Windows CE process owns a 32 MB storage space for code and data. To be able to execute any code, a process must own at least one thread. Windows CE at most 32 processes can run at the same time.

Windows CE Threads

In Windows CE, a thread is a “unit of execution”. In other words, threads are responsible for the execution of code. Within Windows CE, an application consists of one or more threads that are responsible for executing code on the processor. Threads can have different priorities. The Windows CE Operating System amongst others is responsible for scheduling threads. Threads with a higher priority will run before threads with a lower priority. When multiple threads with the same priority exist they will run in a round-robin scheme, giving each thread the same amount of processor time. Threads can voluntarily give up processor time or they can be scheduled out by the operating system. If you target battery powered devices it is very important to do as little processing as possible, because using the processor simply drains the battery. Therefore this high level rule of thumb is very valid: Don’t use the processor unless it is absolutely necessary to do so.

The need for multiple threads inside an application

We kick off our sample code with a very simple application, written in C#. The application has a single form with two buttons and a label.

Our first multithreaded application

When the user clicks the Start button, the label is updated and a WorkerFunction is called. The complete code for the sample looks like this:

using System;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.Data;

using System.Threading;

 

namespace ThreadDemo0

{

  /// <summary>

  /// Summary description for Form1.

  /// </summary>

  public class Form1 : System.Windows.Forms.Form

  {

    private System.Windows.Forms.Label label1;

    private System.Windows.Forms.Button button1;

    private System.Windows.Forms.Button button2;

    private System.Windows.Forms.MainMenu mainMenu1;

 

    private bool workerThreadDone = false;

 

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      //

      // TODO: Add any constructor code after InitializeComponent call

      //

    }

    /// <summary>

    /// Clean up any resources being used.

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      base.Dispose( disposing );

    }

    #region Windows Form Designer generated code

    /// <summary>

    /// Required method for Designer support - do not modify

    /// the contents of this method with the code editor.

    /// </summary>

    private void InitializeComponent()

    {

      this.mainMenu1 = new System.Windows.Forms.MainMenu();

      this.label1 = new System.Windows.Forms.Label();

      this.button1 = new System.Windows.Forms.Button();

      this.button2 = new System.Windows.Forms.Button();

      //

      // label1

      //

      this.label1.Location = new System.Drawing.Point(16, 47);

      this.label1.Size = new System.Drawing.Size(208, 20);

      //

      // button1

      //

      this.button1.Location = new System.Drawing.Point(56, 135);

      this.button1.Size = new System.Drawing.Size(136, 32);

      this.button1.Text = "Start processing";

      this.button1.Click += new System.EventHandler(this.button1_Click);

      //

      // button2

      //

      this.button2.Enabled = false;

      this.button2.Location = new System.Drawing.Point(56, 191);

      this.button2.Size = new System.Drawing.Size(136, 32);

      this.button2.Text = "Stop processing";

      this.button2.Click += new System.EventHandler(this.button2_Click);

      //

      // Form1

      //

      this.Controls.Add(this.label1);

      this.Controls.Add(this.button2);

      this.Controls.Add(this.button1);

      this.Menu = this.mainMenu1;

      this.MinimizeBox = false;

      this.Text = "Thread Demo - Step 0";

 

    }

    #endregion

 

    /// <summary>

    /// The main entry point for the application.

    /// </summary>

 

    static void Main()

    {

      Application.Run(new Form1());

    }

 

    private void button1_Click(object sender, System.EventArgs e)

    {

      label1.Text = "Worker Thread started";

      button1.Enabled = false;

      button2.Enabled = true;

      workerThreadDone = false;

      MyWorkerFunction();

    }

 

    private void button2_Click(object sender, System.EventArgs e)

    {

      workerThreadDone = true;

      label1.Text = "Worker Thread terminated";

      button2.Enabled = false;

      button1.Enabled = true;

    }

 

    private void MyWorkerFunction()

    {

      while (! workerThreadDone)

      {

        // simulate some processing

        Thread.Sleep(1000);

      }

    }

  }

}

The application as shown is list one is a single threaded application, no separate worker threads are started inside the application. That immediately leads to a problem. In the button1_Click event handler the function MyWorkerFunction is called. This function implements a while loop that is terminated when the boolean variable workerThreadDone is set to true. Because of the fact that this application is single threaded, and we are active executing MyWorkerFunction, there is no way that the button2_Click event handler will be called. This means that the application is not responding to button clicks by the user and that there is no way to set workerThreadDone to true. In other words, the application “hangs” in an endless loop and will not terminate properly.

To change this application in a more useful application, we will create a worker thread in which MyWorkerFunction will be executed. In that way, some lengthy processing can be done and at the same time, the user remains in control of the application. For that purpose we only need to change the button1_Click event handler, in which we create and start the worker thread. For better readability, we will also rename the function MyWorkerFunction to MyWorkerThread. The function itself remains the same.

The modified code looks like this (with all other code simply remaining unmodified): 

    private void button1_Click(object sender, System.EventArgs e)

    {

      label1.Text = "Worker Thread started";

      button1.Enabled = false;

      button2.Enabled = true;

      workerThreadDone = false;

      Thread myThread = new Thread(new ThreadStart(MyWorkerThread));

      myThread.Start();

    }

 

    private void MyWorkerThread()

    {

      while (! workerThreadDone)

      {

        // simulate some processing

        Thread.Sleep(1000);

      }

    }

Since the simulated processing is now executed in another thread, the user still has the ability to click buttons, and even to terminate the application. This all because of the fact that the main thread (the one on which the controls are created) is now running in parallel with the worker thread. However, the fact that the user is now capable of closing the application before terminating the worker thread can lead to another problem. Look at the following definition:

A managed application will only terminate when all of its threads have been terminated.

You can easily see that the application is not properly terminated when the worker thread is still running by executing the application from within Visual Studio.NET 2003. Simply deploy to the emulator and run using F5 (using a debug build of the application). Now start the worker thread by clicking the appropriate button and hit the OK button in the upper right hand corner of the application. Even though the form is closed, it appears that Visual Studio.NET 2003 is still actively debugging the application. When you select “Break all” from the Debug menu of Visual Studio.NET 2003 you see that we are breaking on a statement somewhere inside the while loop of MyWorkerThread. A clear indication that the application is still active. To prevent against this behavior we need to extend the application a little. What you need to do is add a Form1_Closing event handler to the application, by selecting the Form properties and double clicking the Closing event. Visual Studio.NET 2003 now adds the Form1_Closing event handler to your source file. We have two different options to implement this event handler. First, when we automatically want all worker threads to terminate when the form is closed, we simply set the boolean workerThreadDone to true inside the Form1_Closing event handler. The event handler will look like this:

    private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)

    {

      workerThreadDone = true;

    }

When you add this event handler as described and run the application again, you will see that it will be properly terminated, maybe after a short delay, since the worker thread might be asleep for anything up to 1000 milliseconds before waking up.

The other option would be to let the user explicitely terminate the worker thread by clicking the "Terminate Thread" button. In this case, when the user requests to close the application we need to show the user a message why the application can not close and cancel the Form1_Closing event. In code this will look like this:

    private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)

    {

      if (! workerThreadDone)

      {

        MessageBox.Show("Stop the worker thread first");

        e.Cancel = true;

      }

    }

Of course the message to the user should be friendlier, but when you run the application now you can see that it will not terminate until the worker thread has been terminated.

 
Back








SpiralFX Technology Solutions
www.spiralfx.com


Do you want to learn developing a full blown Windows Mobile Application? This article and accompanying multimedia content will help you to do so. It will be extended over the upcoming weeks / months, so check back regularly.
 
Read Full Article