July 29 2010




 
Search Blog Entries:



What is this?

Code Details
 
Terminating threads in the .NET CF

Description: 

In embedded environments we often make use of multiple threads to do the work we need to do. There are several reasons for this. An incoming interrupt might need to be serviced as fast as possible. In this case we would probably use a high priority thread in which we perform the necessary actions. In another scenario we might have a slow I/O device with local buffers that needs to be serviced every now and then. In that case a low priority thread to handle the device might bring the right solution. Always we have to keep in mind that timing constraints are met and that a user experiences a system that is responsive.

Inside the .NET Compact Framework we can use the Thread class to create worker threads. Special care has to be taken when terminating a worker thread. An application will only terminate when all of its threads are terminated. To keep in control of terminating threads, it is important to have some mechanism to know from a main thread when worker threads are actually terminated. Since the Join method is not implemented in the Thread class of the .NET CF, we need to come up with a solution to properly terminate worker thread in a synchronized way. The following pseudo code shows a generic pattern on how this can be achieved.

Main Thread:
Create a Mutex with no current owner
bool terminateWorkerThread = false
sleep for a while
terminateWorkerThread = true
Wait for the Mutex
// We now know the worker thread has terminated.

Worker Thread:
Wait for the Mutex
while (! terminateWorkerThread)
{
  Do your work
}
Release the Mutex


In the code snippet, the code in button1_Click event handler shows how to create a worker thread in this way. The code in the button2_Click event handler shows how to properly terminate the worker thread. The worker thread itself exists in the function WorkerThread.

There is an easier way to get the same results if you are using OpenNETCF.org’s Smart Device Framework. The code snippet contains a full working OpenNETCF application. Inside the button3_Click event handler we create another worker thread, this time hosted in the ThreadEx class that comes with the Smart Device Framework. ThreadEx contains a Join method that can be used by any thread to wait until a particular thread is finished. Inside the button4_Click event handler you can see how to use Join. The advantage of this approach is that it is no longer necessary to create a separate Mutex.

Inside the ThreadEx class there is also a method to Abort threads. Even though it is available, you should avoid using it, unless there is really no other alternative. Abort immediately terminates a worker thread without cleaning up resources.

One final point to notice is the way that both worker threads update a UI control. Threads that are not the owner of a UI control can’t directly change the contents of the controls, because it leads to unexpected results and might crash applications at random. Therefor we use a delegate that updates the control on behalf of the worker thread, but the delegate will actually run in the context of the thread created the UI controls.

I encourage you to copy the code snippet, play with it and understand how to properly terminate a multithreaded application. I also strongly advice you to take a good look at the OpenNETCF Smart Device Framework. It contains lots of additional functionality that will make you more productive when developing managed applications for devices.



Code:

namespace ThreadTermination
{
  using System;
  using System.Drawing;
  using System.Collections;
  using System.Windows.Forms;
  using System.Data;
  using System.Threading;
  using OpenNETCF;
  using OpenNETCF.Drawing;
  using OpenNETCF.Windows.Forms;
  using OpenNETCF.Threading;

  /// <summary>
  /// Summary description for Form1.
  /// </summary>
  public class Form1 : System.Windows.Forms.Form
  {
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.Button button2;
    private System.Windows.Forms.ListBox listBox1;
    private System.Windows.Forms.Button button4;
    private System.Windows.Forms.Button button3;

    private
bool
workerThreadDone;
    private bool workerThreadExDone;
    private Thread workerThread;
    private ThreadEx workerThreadEx;
    private Mutex workerThreadRunning;

    public Form1()
    {
      //
      // Required for Windows Form Designer support
      //
      InitializeComponent();
      //
      // TODO: Add any constructor code after
      // InitializeComponent call
      //
      workerThreadDone = false;
      workerThreadExDone = false;
    }

    /// <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.button1 =
          new
System.Windows.Forms.Button();
      this.button2 =
          new
System.Windows.Forms.Button();
      this.listBox1 =
          new
System.Windows.Forms.ListBox();
      this.button4 =
          new
System.Windows.Forms.Button();
      this.button3 =
          new
System.Windows.Forms.Button();
      //
      // button1
      //
      this.button1.Location =
          new
System.Drawing.Point(32, 16);
      this.button1.Size =
          new
System.Drawing.Size(144, 20);
      this.button1.Text = "Start Worker Thread";
      this.button1.Click +=
          new System.EventHandler(this
.button1_Click);
      //
      // button2
      //
      this.button2.Enabled = false;
      this.button2.Location =
          new
System.Drawing.Point(32, 56);
      this.button2.Size =
          new
System.Drawing.Size(144, 20);
      this.button2.Text = "Stop Worker Thread";
      this.button2.Click +=
          new System.EventHandler(this
.button2_Click);
      //
      // button3
      //
      this.button3.Location =
          new
System.Drawing.Point(32, 94);
      this.button3.Size = 
          new
System.Drawing.Size(144, 20);
      this
.button3.Text = "Start Worker ThreadEx";
      this.button3.Click +=
          new System.EventHandler(this
.button3_Click);
      //
      // button4
      //
      this.button4.Enabled = false;
      this.button4.Location =
          new
System.Drawing.Point(32, 134);
      this.button4.Size =
          new
System.Drawing.Size(144, 20);
      this
.button4.Text = "Stop Worker ThreadEx";
      this.button4.Click +=
          new System.EventHandler(this
.button4_Click);
      //
      // listBox1
      //
      this.listBox1.Location =
          new
System.Drawing.Point(208, 16);
      this.listBox1.Size =
          new
System.Drawing.Size(192, 212);
      //
      // Form1
      //
      this.ClientSize =
          new
System.Drawing.Size(434, 248);
      this.Controls.Add(this.listBox1);
      this.Controls.Add(this.button4);
      this.Controls.Add(this.button3);
      this
.Controls.Add(this
.button2);
      this.Controls.Add(this.button1);
      this.Text = "Thread Termination";
    }
    #endregion

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
      ApplicationEx.Run(new Form1());
    }

    private void button1_Click(object sender,
                               System.EventArgs e)
    {
      workerThreadDone = false;
      workerThreadRunning = new Mutex(false);
      workerThread =
         
new Thread(new ThreadStart(WorkerThread));
      workerThread.Start();
      button1.Enabled = false;
      button2.Enabled = true;
    }

    private void button2_Click(object sender,
                               System.EventArgs e)
    {
      workerThreadDone = true;
      // Wait until the worker thread is done.
      // In that case we get the Mutex
      workerThreadRunning.WaitOne();
      workerThreadRunning.ReleaseMutex();
      button1.Enabled = true;
      button2.Enabled = false;
    }

    private void button3_Click(object sender,
                               System.EventArgs e)
    {
      workerThreadExDone = false;
      workerThreadEx =
          new ThreadEx(new ThreadStart(WorkerThreadEx));
      workerThreadEx.Start();
      button3.Enabled = false;
      button4.Enabled = true;
    }

    private void button4_Click(object sender,
                               System.EventArgs e)
    {
      workerThreadExDone = true;
      // Wait until the worker thread is done by 
      // Joining the worker thread.
      workerThreadEx.Join(2000);
      button3.Enabled = true;
      button4.Enabled = false;
    }

    private void WorkerThread()
    {
      // Immediately grep the mutex once we start running.
      // Since our 
parent wants to grep the mutex once it
      // stops the worker thread, 
the parent will block
      // until we are entirely done.
      workerThreadRunning.WaitOne();
      // Run the worker thread continuously
      // until we are done.
      while (! workerThreadDone)
      {
        this.Invoke(new EventHandler(UpdateListBox));
        Thread.Sleep(1000);
      }
      // Don't forget to release the mutex as the last
      // action in the worker thread. 
Failing to do so
      // will block the parent thread as well, since it
      // will be 
waiting for the mutex.
      workerThreadRunning.ReleaseMutex();
    }

    private void WorkerThreadEx()
    {
      // Run the worker thread continuously
      // until we are done.
      while (! workerThreadExDone)
      {
        this.Invoke(new EventHandler(UpdateListBox));
        Thread.Sleep(1000);
      }
    }

    private void UpdateListBox(object sender, 
                               EventArgs e)
    {
      listBox1.Items.Add("Worker thread running");
    }
  }
}







Send us your solutions, code, or advice. We might put it here on the site!
 
Back








SpiralFX Web Development
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