Snippet - ThreadPool for NETMF

I just posted ThreadPool for NETMF on Codeshare. Feel free to discuss and make suggestions here.

5 Likes

Good one! Thanks!

@ Reinhard Ostermeier - Very useful. Thank you!

This is very interesting. Have you tried measuring performance increase, if there is any?

@ Simon from Vilnius -

I made some Performance tests using the following code:

var doneEvent = new ManualResetEvent(false);

int dummy = ThreadPool.GetMaxThreads();

int threadPoolTotal = 0;

for (int n = 0; n < 10000; ++n)
{
   var ts = DateTime.Now;
   ThreadPool.QueueUserWorkItem(_ =>
   {
      var dt = DateTime.Now - ts;
      threadPoolTotal += dt.Milliseconds;
      doneEvent.Set();
   });

   doneEvent.WaitOne();
   doneEvent.Reset();
}

Debug.Print("ThreadPool total: " + threadPoolTotal + "ms");
Debug.Print("Average ThreadPool startup time: " + threadPoolTotal / 10000.0 + "ms");

int classicTotal = 0;
for (int n = 0; n < 1000; ++n)
{
   var ts = DateTime.Now;
   var thread = new Thread(() =>
   {
      var dt = DateTime.Now - ts;
      classicTotal += dt.Milliseconds;
      doneEvent.Set();
   });
   thread.Start();

   doneEvent.WaitOne();
   doneEvent.Reset();
}

Debug.Print("Classic thread total: " + classicTotal + "ms");
Debug.Print("Average classic thread startup time: " + classicTotal / 1000.0 + "ms");

This code measures the time to setup and start the thread.
Unfortuenatelly I have no crystal on my G120HDR so I could not use the RealtimeClock.

With an attached Debugger the results are as expected:
Average time for ThreadPool: 1.0054ms (over 10000 samples)
Average time for classic threads: 36,119ms (over 1000 samples)

But when I run the program without Debugger i get:
Average time for ThreadPool: 2.0000ms (over 10000 samples)
Average time for classic threads: 0,000ms (over 1000 samples)

This would mean my ThreadPool is even slower without debugger and a classic thread starts in notime !?
Because the total time for ThreadPool is always exact 20000 I think something is wrong here, but I can’t tell what.

@ Reinhard Ostermeier -
Try measuring the total elapsed time around the for loop. This will include execution time of other stuff besides thread setup, but might shed some light on why the non debug numbers are so weird.

It could be that DateTime.Now isn’t precise enough (or updated frequently enough) if things really are happening fast. This is often the case in desktop apps.

Also to measure time with max granularity use the DateTime.Now.Ticks value, this increments in units of 0.1 microseconds - that’s on a desktop with hi res timer anyway, I’m assuming its the same on a Gadgeteer mainboard.

Thanks for sharing…

Schaut gut aus…

If DateTime.Now isn’t good enough, then DateTime.Now.Ticks (necessarily) won’t be good enough.

.Ticks is quite precise, but I don’t know how accurate it is. Desktops have high resolution timers (as exposed in System.Diagnostics.Stopwatch), but your micro won’t be anywhere near as precise.

TimeSpan.TicksPerSecond will give you a readout of how fine the tick count is on any given system.

But no matter how good the resolution is / how many tics per second you have. This does not show how often this timer gets updated.
Also, the managed function call to get this value takes usually way longer than the ticks resolution.

Will this example work (with minor modifications…) with Reinhards code?

[url]Microsoft Learn: Build skills that open doors in your career

My thread pool should work exactly like the .NET variant API wise.
Internally it might not work exactly the same.
Here is how it works.
I create the minimum number of threads on startup (in static constructor).
Each thread is started and waits on an ManualResetEvent.
When function is queued i go through the list and look for a waiting thread, which gets assigned the function and the event is set.
If no thread is available, i start a new thread as long as the maximum number of threads is not reached.
If the maximum number of threads is busy, I put the function in an queue.
If a thread/function is finished, and there are functions in the queue, then the thread immediately continues working on that function.

Here is a difference to the .NET implementation:
If there is an unhandled exception in the worker function, I do not let the system crash. I catch it and fire an event you can sign up to.
I think I also have an event which is fired every time a worker function is finished.

One little problem with my implementation:
If you have a lot of long running worker functions, (or some that never returns) then you might run into the maximum number of threads, this will slow down execution of new workers, or even worse, if you have all threads with not returning workers busy, no more new workers will be executed.
But there is also an event that can notify you every time the maximum number of threads is busy.

Does anyone at GHI know more about the implementation of Ticks? On Windows we can use (native) QueryPerformanceCounter and QueryPerformanceFrequency to measure time intervals very precisely but these dont exist under .Net MF of course.

Bear in mind too that the Raptor (for example) runs at 400 MHz (period of 2.5 nS) if that signal (or a derivative of it) is available then that’s a great basis for some kind of hi res stuff…

This kind of stuff is very helpful when trying to do performance profiling.