Click here to Skip to main content
15,885,782 members
Articles / Netduino

Multithreading with Netduino and .NET Microframework

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
11 Jul 2014CPOL5 min read 25.6K   13   2
.NET Microframework makes multithreading with Netduino easy job. Let’s have a look how.

First, we’ll use “standard” way. In our first example, we will just create and fire new thread. It will execute in parallel with main thread:

C++
public class Program
{
	public static void Main()
	{
		Thread someThread = new Thread(SomeThreadMethod);

		// Start the thread execution
		someThread.Start();

		// Main thread code continues
		...

		// Exiting main thread will stop someThread
	}

	private static void SomeThreadMethod()
	{
		// The thread's code
		...
	}
}

In the second example, we will execute the thread and will suspend the main thread until we resume it:

C++
public class Program
{
	private static Thread mainThread;

	public static void Main()
	{
		// Get handle to main thread
		mainThread = Thread.CurrentThread;

		Thread someThread = new Thread(SomeThreadMethod);

		// Start the thread execution
		someThread.Start();

		// Suspend main thread execution
		mainThread.Suspend();

		//Following code will be executed after main thread is resumed
		...
	}

	private static void SomeThreadMethod()
	{
		// The thread code
		...

		// Resume main thread
		mainThread.Resume();
	}
}

Thread class contains some useful properties and methods. For example, using Priority property we can control how the thread should be scheduled compare to the other threads.

The second example contains elements of thread synchronisation. Although, it is still OK to use Suspend() and Resume() for synchronising threads, it is recommended not using them. They are causing some problems, especially when things come to sharing resources between threads. .NET Microframework provides bunch of classes to help us avoid this problems.

For example, there is a problem with sharing resources when using those methods. Fortunately, there are several ways to solve this. We will have to look on them separately as they are solving different problems.

At the beginning, we’ll look at a method attribute MethodImpl and more specifically MethodImplOptions enumeration. Let’s say we have a method, called by multiple threads, that is doing some unique work and it have to finish it’s execution before next thread can call it. For this case we can attribute the method like this:

C++
[MethodImpl(MethodImplOptions.Synchronized)]
private void SomeSynchronizedMethod()
{
     // Method's code
     ...
}

All threads trying to access this method will be blocked for the time the method is in use from other thread.

In the event that there are enough scenarios that justify existence of this approach, you can see many ones that this is absolutely inappropriate. A better approach is to lock the resources that methods are sharing. For this there is a class called Monitor. Using this class is easy:

C++
private static SomeThreadMethod()
{
     ...
     try
     {
          Monitor.Enter(someSharedResource);

          //access shared resource here
          ...
     }
     finally
     {
          Monitor.Exit(someSharedResource);
          ...
     }
     ...
}

private static SomeOtherThreadMethod()
{
     ...
     try
     {
          Monitor.Enter(someSharedResource);

          //access shared resource here
          ...
     }
     finally
     {
          Monitor.Exit(someSharedResource);
          ...
     }
     ...
}

Now we can execute both methods in parallel. When one of them reaches Monitor.Enter() it gets access to the shared resource and then performs operations with it. At this point, if other methods call Monitor.Enter() to access same resource, it will be blocked until first method execute Monitor.Exit() to release the resource. Note that Monitor.Exit() is in finally block of exception handler. This is best practice; otherwise, if exception occurs within the method, the resource may stay locked forever.

This, you may know, is equivalent to C#’s lock keyword. When compiler meets:

C++
lock(someSharedResource)
{
     // access shared resource here
     ...
}

it compiles it to:

C++
try
{
     Monitor.Enter(someSharedResource);

     //access shared resource here
     ...
}
finally
{
     Monitor.Exit(someSharedResource);
}

The Monitor and Thread classes are part of System.Threading namespace. As I mentioned before, there are few other classes and each one of them provides us with different way to solve our problems. As conclusion of this part, we will have a quick look on how to use Interlocked class and its members. It is simple as this:

C++
public class Program
{
     private static int someSharedResource;
     ...

     private static void SomeThreadMethod()
     {
          //...
          Interlocked.Increment(ref someSharedResource);
          //...
     }

     private static void SomeOtherThreadMethod()
     {
          //...
          Interlocked.Increment(ref someSharedResource);
          //...
     }
}

or

C++
public class SharedResources
{
     public static int SomeSharedResource;
}

public class Program
{
     private static void SomeThreadMethod()
     {
          //...
          Interlocked.Increment(ref SharedResources.SomeSharedResource);
          //...
     }

     private static void SomeOtherThreadMethod()
     {
          //...
          Interlocked.Decrement(ref SharedResources.SomeSharedResource);
          //...
     }
}

Exchange() and CompareExchange() provides some interesting possibilities, so spend few moments to read the documentation.

We are not done yet with the subject of multithreading, so be ready for more to come in next part.

In addition to the previously mentioned methods for synchronisation of threads, Microframework provided two classes to do this, using events: AutoResetEvent and ManualResetEvent. Both classes inherit WaitHandle and only differ by the fact that you have to manually reset status when using ManualResetEvent. Let’s see some examples of using those classes:

C++
public class Program
{
     // Initialise auto reset event with not signalled (false) initial state
     private static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

     static void Main()
     {
          Thread someThread = new Thread(SomeMethod);
          someThread.Start();
          ...
          // Waiting for thread's notification
          autoResetEvent.WaitOne();
          ...
          // This is auto reset event so no reset is required
          someThread.Start();
          autoResetEvent.WaitOne();
          ...
     }

     private static void SomeMethod()
     {
          ...
          // Send notification
          autoResetEvent.Set();
     }
}

or

C++
public class Program
{
     // Initialise manual reset event with not signalled (false) initial state
     private static ManualResetEvent namualResetEvent = new ManualResetEvent(false);

     static void Main()
     {
          Thread someThread = new Thread(SomeMethod);
          someThread.Start();
          ...
          // Waiting for thread's notification
          namualResetEvent.WaitOne();
          ...
          // This is manual reset event so reset is required
          namualResetEvent.Reset();
          someThread.Start();
          namualResetEvent.WaitOne();
          ...
     }

     private static void SomeMethod()
     {
          ...
          // Send notification
          namualResetEvent.Set();
     }
}

WaitHandle class (thus both AutoResetEvent and ManualResetEvent classes too) have two more interesting and flexible methods – WaitAll() and WaitAny(). First waits for all the elements in the specified array to receive a signal and second waits for any of the elements to receive a signal. Note that they accept array of WaithHandles thus way you can create and work with many threads and synchronise between them. Both methods has overrides that allow specifying the number of milliseconds to wait and parameter to specify shall thread(s) to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it afterwards.

You can set the thread priority using ThreadPriority enumeration and check thread status using ThreadState enumeration. TheadState allows you to perform bitwise operations so you can check for multiple statuses:

C++
if (someThread.ThreadState == (ThreadState.Suspended | ThreadState.Stopped))
{
     ...
}

Lastly, we must not leave Timer class without attention :) This is very useful class that “Provides a mechanism for executing a method at specified intervals”. It is simple as it states. Timer’s constructor uses TimerCallback delegate for the method you want to execute and there are two handy overrides, which allow us to specify times intervals using int (in milliseconds) or TimeSpan. The second parameter is also useful in case you want to pass additional information to the executed method. Keep it null if no information is to be passed.

C++
public class Program
{
     static void Main()
     {
          // Initial delay 5s, invoke every 1 second after that
          var someTimer = new Timer(new TimerCallback(SomeMethod), "Some data", 5000, 1000);
          // Or
          //var someTimer = new Timer(new TimerCallback(SomeMethod), "Some data", new TimeSpan(0, 0, 0, 5), new TimeSpan(0, 0, 0, 1));
          ...
          Console.ReadLine();
     }

     private static void SomeMethod(object data)
     {
          ...
          Console.WriteLine((string)data);
          ...
     }
}

One good thing about last version Microframework is that we can use lambda expressions:

C++
public class Program
{
     static void Main()
     {
          var someTimer = new Timer(new TimerCallback(
               (object data) =>
               {
                    Console.WriteLine((string)data);
               }),
               "Some data", new TimeSpan(0, 0, 0, 5), new TimeSpan(0, 0, 0, 1));
          ...
          Console.ReadLine();
     }
}

Last note to take is that Microframework’s System.Threading namespace does not implement all the classes of the full .NET framework System.Threading.

Now I want to show you one more way to synchronise threads. It is more like “waiting” a thread to finish, before continues with execution of the code in the calling thread. I have seen people usually do this in following manner:

C++
public class Program
{
     public static void Main()
     {
        Thread someThread = new Thread(SomeThreadMethod);

        // Start the thread execution
        someThread.Start();

        // Put main thread into infinite sleep
        Thread.Sleep(Timeout.Infinite);

        // Main thread never resumes!
     }

     private static void SomeThreadMethod()
     {
          // The thread's code
          ...
     }
}

Well it might look OK and it works OK but it is somehow “ugly” and you lose control over the main thread continuation. .NETMF provides nice way of doing this:

C++
public class Program
{
     public static void Main()
     {
          Thread someThread = new Thread(SomeThreadMethod);

          // Start the thread execution
          someThread.Start();

          // Tell main tread to wait until someThread finish its execution
          someThread.Join();

          // Main thread code continues
          ...
     }

     private static void SomeThreadMethod()
     {
          // The thread's code
          ...
     }
}

Now that’s better! You still may block the app from exiting: just never return from SomeThreadMethod(). But anytime you decide to change algorithm you can do it easily. Another advantage of Join() method is that you do not have to do event synchronising thus saving yourself writing few more lines of code.

Here also one very quick way just to drop some thread to do something:

C++
public class Program
{
     public static void Main()
     {
          new Thread(
              () =>
                  Debug.Print("Hello ...")
              ) { Priority = ThreadPriority.AboveNormal }
              .Start();

          new Thread(
              () =>
              {
                   Debug.Print("Line 1");
                   Debug.Print("Line 2");
              }) { Priority = ThreadPriority.Lowest }
              .Start();

          // Main thread code continues
          ...
     }

     private static void SomeThreadMethod()
     {
          // The thread's code
          ...
     }
}

License

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


Written By
Founder DataLich Ltd
United Kingdom United Kingdom
https://www.garder.me
https://datalich.com

http://ghadzhigeorgiev.wordpress.com

Comments and Discussions

 
QuestionCool, but some suggestions Pin
Marc Clifton11-Jul-14 9:26
mvaMarc Clifton11-Jul-14 9:26 
AnswerRe: Cool, but some suggestions Pin
Georgi Hadzhigeorgiev14-Jul-14 9:30
Georgi Hadzhigeorgiev14-Jul-14 9:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.