ThreadPool

Wed, February 23, 2005, 02:57 PM under MobileAndEmbedded
No doubt you are familiar with the System.Threading.ThreadPool (if not, go learn about it now!). The Compact Framework implementation only offers a single method: QueueUserWorkItem(WaitCallback, Object). Basically, every time you call this method it will call you back on a different thread, passing back to you the state object you gave it, so you can run your background work. Once the background work is finished (i.e. the method returns), the thread is then either reused in a future call or is removed from the pool (after a undefined interval). The advantage over explicitly creating threads is obvious in such a thread-reuse scenario (less context switching, no thread creation/destruction overhead each time, ease of use).

Internally, the NETCF ThreadPool is built on the System.Threading.Timer (whose core implementation is in native code). In other words starting a one shot timer with dueTime of 0 is the same as queuing a work item with the ThreadPool. Clearly using the ThreadPool is the preferred choice (in other words don't use the Threading.Timer just for going async, rather use it in scenarios where timing is truly required).

Apart from the different implementations, the desktop and CF ThreadPool have other differences.

1. The documentation states that explicitly using a thread is a better alternative to the ThreadPool for lengthy operations i.e. use the ThreadPool for short-lived tasks. One of the reasons it advises this is because the desktop implementation has a ThreadPool limit of 25 per process (so, for example, you can imagine deadlock situations if you starve the ThreadPool of its 25 threads and e.g. two of them are waiting to acquire the same resource). However, the NETCF 1.0 ThreadPool has a limit of 256 (treat that as if there was no limit; for a laugh, I tried testing the limit and my device stopped responding around ~150). Note that CF 2.0 rectifies this by setting the limit to 25, bringing parity with the desktop. On the desktop with .NET 1.1 you can change it if you really try hard and with .NET 2.0 there is even a method (SetMaxThreads). I am not sure if we will see this method in the CF 2.0 RTM, but for the November CTP you can control the number of threads via the registry: HKLM\Software\Microsoft\.NETCompactFramework\ThreadPool\MaxThreads

2. The desktop's ThreadPool threads are all background. Since the CF 1.0 does not support background threads, the difference is obvious. As a reminder, a non-background thread can keep your process up even if the main (UI) thread has exited. Since CF 2.0 supports Thread.IsBackground, the ThreadPool threads will return true for that property.

3. ThreadPool threads, like other managed threads, are created at the default priority of Normal (251 in WinCE terms). I have discussed previously about "Threads and ThreadPriority with the .NET Compact Framework". If (against my advice in that post) you change the priority of ThreadPool threads (which btw can only be done in the callback), you will find that the priority may not be reverted when the thread is returned to the ThreadPool (MSFT quote). In other words, future uses of the ThreadPool could run on that thread with whatever priority you gave it in a different context (dangerous!). This is not true for the Full Framework and, fortunately, CF 2.0 brings parity with the desktop.

So we can see all the differences between CF 1.0 and Full Fx 1.1, but we also see how CF 2.0 eliminates them.

Finally, CF 1.0 does not support Control.BeginInvoke but CF 2.0, like the desktop, does. BeginInvoke indeed uses threads from the ThreadPool on both platforms.
Comments are closed.