Welcome to MSDN Blogs Sign in | Join | Help

Thread Management In the .NET Micro Framework

Thread Management In the .NET Micro Framework

Igor Grebnev

 

The .NET Micro Framework provides support for multithreading, just as its larger cousins (the .NET Framework and the .NET Compact Framework) do. In today’s article, I’ll be explaining multithreading in the .NET Micro Framework. I’ll show some common techniques for setting and retrieving priorities, scheduling, and suspending their tasks. 

Setting and Retrieving Thread Priorities

In the .NET Micro Framework, always remember that thread priorities are relative to one another. Specifically, your application sets and retrieves a thread’s priority through the Priority property of Thread class. On the current thread, your program retrieves the thread’s priority using Thread.CurrentThread.Priority. When it does, the Thread.CurrentThread.Priority always returns the enumerated value ThreadPriority.Normal, which has an integer value of 2, no matter what the priority actually is. The reason for this is that the current thread’s priority is calculated relative to the priority of the thread that executed the call to retrieve the property. If the calling thread and the thread being tested are the same, the priority is always ThreadPriority.Normal.

Let’s look into this in more detail. Each thread internally has a data member containing a priority number with a range from 0 to 4. A priority of 0 is lowest, a priority of 2 is normal, and a priority of 4 is highest. This value is stored internally in the framework.

Suppose, for example that a managed code application retrieves the priority of a thread named myWorkerThread. The result is calculated as follows:

myWorkerThread.Priority = myWorkerThread.internalPriority – CurrentThread.internalPriority + 2

where the CurrentThread.internalPriority is priority of the thread that makes the call. If the thread checks its own priority, the result is always ThreadPriority.Normal because myWorkerThread.internalPriority and CurrentThread.internalPriority are the same value.

If, instead, your program checks the priority of one thread from another thread, the priority of the thread being checked is always given relative to the calling thread. So if a calling thread with a priority of 2 checks the priority of a thread with a priority of 4, your program gets an answer of 4 (4-2+2).

Similar calculations are applied on setting of thread priority – the priority is set relative to the priority of the thread that makes the call. The internal priority used by the scheduler is calculated as follows:

myWorkerThread.internalPriority = CurrentThread.internalPriority – 2 + myWorkerThread.Priority

As an example, if a thread with Highest priority sets another thread’s priority to Normal, the resulting thread priority will be Highest. This can be disconcerting when you’re not used to it. The simple way to avoid problems with thread priorities is to always assign thread priorities from a thread that has a priority of Normal.

Scheduling Threads with Different Priorities

Assigning priorities to threads affects how those threads are scheduled. Suppose, for instance, you have two threads. If one thread is assigned a priority of Highest and the other is assigned a value of Normal , they get almost equal CPU time - except of at the very beginning of execution.

On the other hand, if there are two threads with the priorities of Highest and Lowest (the integer values 4 and 1, respectively) then the thread with Highest priority gets 2 times more CPU time than the thread with Lowest priority. Basically, the scheduler gives two CPU time slices to go to Highest priority thread followed by one time slice to Lowest priority thread.

Suspending and Stopping a Thread

When pausing a thread’s execution, be aware that calls to the Thread.Suspend method do not stop the thread immediately. After the call, the thread is put into a “suspended” state, but it will continue to execute until its time slice expires.

Consider the following example:

Debug.Print( “Suspending current thread” );

Thread.CurrentThread.Suspend();

Debug.Print( “Current thread executing” );

 

If you look at the debug output of this code, you’ll see that “Current thread executing” is printed immediately after the call to Suspend. The thread continues to run until its time slice expires. That can take up to 20 milliseconds. When the time slice expires, the thread becomes suspended.

If you need to have a thread suspended immediately, you can call Thread.CurrentThread.Sleep and pass it the value 0 in its parameter list. This stops the thread regardless of whether or not its time slice has expired. The following code demonstrates this technique.

Debug.Print( “Suspending current thread” );

Thread.CurrentThread.Suspend();

Thread.CurrentThread.Sleep(0);

Debug.Print( “Current thread executing” );

 

In this case, the execution of a current thread is blocked as soon as the command Sleep(0) is executed. The suspended thread resumes once other thread calls Resume for it.

Weaving It All Together

Although thread management can be a little tricky, understanding the basic techniques presented in this article will go a long way toward simplifying things.

 

IIC China Shenzhen and IIC China Shanghai

"Microsoft® .NET Micro Framework.NET的可靠性能和卓越效率與Microsoft® Visual Studio的高生力合而為一,方便用户为規模較小、價格較廉、資源有限的設備來編製程式。"

 

The .NET Micro Framework team joined the embedded/IC conferences in China for the first time!  We had the pleasure of appearing at the IIC China Shenzhen(Mar3-4) and the IIC China Shanghai( Mar 10-11). Most of the people in the Chinese technical community haven't heard about us. However, they showed great interest in our product, gave good comments, and see lots of potential in adopting our platform. People are excited about the ideas of using .NET to write embedded application instead of developing in C++.

 

Shenzhen pictures:

     

Shanghai pictures:

     

 

Using the Dispatcher

 

Background

The typical Micro Framework Presentation application will have at least two threads.  The first thread, which is explicitly created by the developer, usually handles I/O to one or more hardware peripherals.  The second thread, created and managed implicitly by the CLR, handles all UI operations such as drawing of UI elements such as controls and windows.  This UI thread is also known as the Dispatcher and its purpose is to access the UI elements in a thread safe manner. 

This article will discuss several techniques for executing code on Dispatcher thread, but first it will take a look at a common mistake that many developers make.

The Timer Application

Figure 1 shows the main part of the Timer application source code.  This should be familiar territory to most MF developers.  It has a main that creates an application, the main window and finally starts the application by calling the Run method with main window object.  The main window consists of a single Panel object that contains a single Text control.  The text control will display the amount of time that has elapsed since the program started. 

In the CreateWindow method a Timer object is created.  The Timer object will be responsible for updating the timerText control every second.  All of the samples in this article will implement a different Timer solution, so the main UI code can remain the same throughout the discussion.

public class TimerApp : Microsoft.SPOT.Application

{

    public static void Main()

    {

        TimerApp myApplication = new TimerApp();

        Window mainWindow = myApplication.CreateWindow();

        // Start the application

        myApplication.Run(mainWindow);

    }

 

    private Window mainWindow;

 

    private Text timerText;

    private Timer timer;

       

    public Window CreateWindow()

    {

        // Create a window object and set its size to the

        // size of the display.

        mainWindow = new Window();

        mainWindow.Height = SystemMetrics.ScreenHeight;

        mainWindow.Width = SystemMetrics.ScreenWidth;

 

        Panel panel = new Panel();

 

        timerText = new Text();

        timerText.Font = Resources.GetFont(Resources.FontResources.small);

        timerText.HorizontalAlignment = HorizontalAlignment.Center;

        timerText.VerticalAlignment = VerticalAlignment.Center;

           

        panel.Children.Add(timerText);

 

        // Add the text control to the window.

        mainWindow.Child = panel;

        // Set the window visibility to visible.

        mainWindow.Visibility = Visibility.Visible;

 

        // Create a new timer object and start it.

        timer = new Timer(timerText);

        timer.Start();

 

        return mainWindow;

    }       

}

Figure 1 Timer Application

Figure 2 shows what the application looks like when run using the Sample Emulator from the SDK.

Emulator View

Figure 2 Emulator View

The Wrong Way

Here is the first implementation of the Timer object that was mentioned in the previous section.  This version will simply spin off a thread and give that thread a reference to the Text control.  The thread will update the Text control’s TextContent property then go to sleep for one second. 

Figure 3 provides the code for the Timer object.  The Timer object’s constructor caches a reference to the Text control and sets the start time for the Timer as a DateTime.  The Start method simply spins off the thread.  The UpdateTimerTextThread is the thread start method and it does the work of updating the Text control and then going to sleep.

public class Timer

{

    private Text textView;

    private DateTime start;

    private Thread timerThread;

 

    public Timer(Text text)

    {

        textView = text;

        textView.TextContent = new TimeSpan(0).ToString();

        start = DateTime.Now;

    }

 

    public void Start()

    {

        // Spin off a thread to update the timerText every second.

        timerThread = new Thread(new ThreadStart(UpdateTimerTextThread));

        timerThread.Start();

    }

 

    protected void UpdateTimerTextThread()

    {

        while (true)

        {

            textView.TextContent =

                ((TimeSpan)DateTime.Now.Subtract(start)).ToString();

            Thread.Sleep(1000);

        }

    }

}

Figure 3 Timer1 - Simple Thread

The rendering of the UI controls is done on the Dispatcher thread, which is different from the Timer’s thread.  Since the worker thread is modifying a shared resource; in this case the Text control, this program will produce unexpected results.  Experiments with this example produced three different outcomes. 

1.       The Text control never updated and always displayed “00:00:00”.

2.       The Text control width never updated and displayed “00:00:…”, where the ellipsis were added automatically by the control renderer because the control width was too short.

3.       The program functioned normally.

Programmers typically want their program to work correctly and to work correctly every time it is run.  The next example will show how to fix this problem.

Synchronizing the Two Threads

Since the problem appears to be related to multiple threads some sort of thread synchronization method must be employed.  The Dispatcher does not expose any sorts of locks that could be used, but it does expose a method to execute code on the Dispatcher thread; causing all access to the UI control to occur on the Dispatcher thread.  Three items are needed to implement this solution.

1.       A delegate to use as a callback for the Dispatcher.

2.       A method to implement the delegate

3.       A call to the Dispatcher.Invoke method

Figure 4 provides an implementation of all three requirements.  The UpdateTextDelegate provides the delegate and the UpdateTimerText method implements the delegate.  Notice that the UpdateTimerText method simply updates the Text control’s TextContent property.

public class Timer

{

    private Text textView;

    private DateTime start;

    private Thread timerThread;

    private delegate void UpdateTextDelegate();

 

    public Timer(Text text)

    {

        textView = text;

        textView.TextContent = new TimeSpan(0).ToString();

        start = DateTime.Now;

    }

 

    public void Start()

    {

        // Spin off a thread to update the timerText

        // every second.

        timerThread = new Thread(

            new ThreadStart(UpdateTimerTextThread));

        timerThread.Start();

    }

 

    public void UpdateTimerText()

    {

        textView.TextContent =

            ((TimeSpan)DateTime.Now.Subtract(start)).ToString();

    }

 

    protected void UpdateTimerTextThread()

    {

        while (true)

        {

            textView.Dispatcher.Invoke(

                new TimeSpan(0, 0, 1),

                new UpdateTextDelegate(UpdateTimerText));

            Thread.Sleep(1000);

        }

    }

}

Figure 4 Timer2 - Thread with Dispatcher

This solution also spins off a thread that causes the Text control to be updated with the new elapsed time every second.  This one differs in that it calls the Invoke method of Text control’s Dispatcher object.  In this case the Invoke method takes two arguments.  In actuality the Invoke method takes three arguments.  Here is the definition of Invoke as taken from MSDN.

Syntax

C# 

public Object Invoke (

         TimeSpan timeout,

         Delegate method,

         Object[] args

)

Parameters

timeout

The maximum amount of time the program will wait for the operation to finish.

method

A delegate to a method that takes multiple arguments, which is pushed onto the Dispatcher object's event queue.

args

[ParamArrayAttribute] An object to be passed as an argument to the specified method. This can be a null reference if no arguments are needed.

Return Value

The return value from the invoked delegate, or a null reference if the delegate has no return value.

Figure 5 Invoke Documentation

 

In the example here, the delegate does not take any arguments, so the third argument is omitted.  This program only updates the Text control every one second, so it is willing to wait for the UpdateTimerText method to be invoked for one second before it times out.  The timeout parameter is important because the Invoke method is synchronous, meaning that it will not return until the call to the delegate has completed.

Calls to the Dispatcher thread can also be performed asynchronously using the BeginInvoke method.  The BeginInvoke method does not take a timeout parameter and returns a DispatcherOperation object.  The DispatcherOperation can be used to monitor and control the invocation of your delegate method.  For instance you can get the result from the call at a later time or abort the operation altogether.

The NewPresentation sample that ships in the .NET Micro Framework SDK v2.5 uses the asynchronous call BeginInvoke in the GPIO Button’s interrupt handler.  This allows the interrupt thread to return right away to handle more button presses or other hardware input.

This solution works great and is not too hard to implement, but there is actually an easier method built into the Framework that will be discussed in the next section.

The DispatcherTimer Class

The DispatcherTimer is a timer that is fully integrated into the Dispatcher’s queue.  Figure 6 provides an example that uses the DispatcherTimer.  Notice that in the Timer.Start method a thread was not spun off as was done in past examples.  Instead a DispatcherTimer object is instantiated and the Dispatcher for the Text control is passed in during object creation.  Next, the Tick event is hooked up with our UpdateTimerText call back.  Lastly, the DispatchTimer object’s Interval property is set to one second and the timer is started by calling the Start method.  The Interval defines the period at which the timer event will fire; in this case one second.

The signature for UpdateTimeText had to change to match the EventHandler delegate definition, which takes two arguments.  Again, the UpdateTimeText updates the Text control’s TextContent property and returns.

public class Timer

{

    private Text textView;

    private DateTime start;

    private DispatcherTimer dispatchTimer;

 

    public Timer(Text text)

    {

        textView = text;

        textView.TextContent = new TimeSpan(0).ToString();

        start = DateTime.Now;

    }

 

    public void Start()

    {

        // Create a dispatcher timer to update the timer text

        // every second.

        dispatchTimer = new DispatcherTimer(textView.Dispatcher);

        dispatchTimer.Tick += new EventHandler(UpdateTimerText);

        dispatchTimer.Interval = new TimeSpan(0, 0, 1);

        dispatchTimer.Start();

    }

 

    public void UpdateTimerText(object sender, EventArgs e)

    {

        textView.TextContent =

            ((TimeSpan)DateTime.Now.Subtract(start)).ToString();

    }

}

Figure 6 Timer3 - DispatcherTimer

The Temperature sample that shipped with the .NET Micro Framework 2.5 SDK contains a more complete example using the DispatcherTimer with simulated hardware.

Conclusion

All access to UI Elements, such as Windows, Panels and Controls must be done by the Dispatcher Thread.  There are two methods to execute code on the Dispatcher thread.  One method is to call the Invoke method for the UI Elements Dispatcher.  The other method is to use the DispatcherTimer.

Embedded World 2008 Part 2

The team is just back from another successful Embedded World in Nuremberg, Germany. This is our one-year anniversary from our launch at this conference last year. It was good to go back and think about our progress. When we were there a year ago, there were just a few development platforms and no commercial products out on .NET Micro Framework yet. This year, our presentation area was overflowing. Apologies to Lorenzo for the blurry picture)

 

 

Embedded World marked the release of 2.5 with our native TCP/IP stack and much of the DPWS functionality (at least in Beta) as well as a number of porting kit changes. The release was well received and the booth was always busy.

 

 

Lots of good press and partner meetings as well. I believe the story got picked up in various forms by over 70 publications. We passed out nearly all the product information sheets that we took over there – both for .NET Micro Framework and for our partners.

It was good to be there but it was certainly good to get home as well. Going home almost didn’t happen. Below is the departure display in the Amsterdam Airport the day I flew back. If you can read it, you’ll see that nearly every flight that day was delayed or cancelled due to excessive winds. Fortunately, my flight snuck out.

 

 

Our next show is ESC West in San Jose in April. Perhaps we will see you there.

 

Colin

Embedded World 2008

The .NET Micro Framework team will return to Embedded World in Nuremberg, Germany, Tuesday, February 26 through Thursday, February 28. Visit us in Hall 11.0 at Stand 318. Come check out the latest .NET Micro Framework devices and join Colin Miller as he presents "Connecting Devices with .NET Micro Framework v2.5" in the Microsoft theater from 3:00 - 3:45pm on  February 26, 27, and 28. Jim Mateer will lead an hour-long conference session entitled "Web Services for Devices and the .NET Micro Framework" at 10am on Tuesday, February 26 in the Exhibitors Forum, Hall 11.

Microsoft .NET Micro Framework Newsletter - Vol. 1 No. 2

Reprint of the second .NET Micro Framework newsletter. Subscribe here!

 

Sign up for other newsletters | Unsubscribe | Update your profile

 

Quarterly Newsletter

Volume 1, Number 2

December 2007