views:

2900

answers:

8

In multithreaded .NET programming, what are the decision criteria for using ThreadPool.QueueUserWorkItem versus starting my own thread via new Thread() and Thread.Start()?

In a server app (let's say, an ASP.NET app or a WCF service) I think the ThreadPool is always there and available. What about in a client app, like a WinForms or WPF app? Is there a cost to spin up the thread pool? If I just want 3 or 4 threads to work for a short period on some computation, is it better to QUWI or to Thread.Start().

+1  A: 

This discussion will interest you
When should I not use the ThreadPool in .Net?

Cherian
I didn't really benefit from that discussion, which seemed to be mostly a debate about the cost of starting a thread.
Cheeso
A: 

The thread pool is always available in .NET apps, regardless of what kind.

The cost for starting a thread from the thread pool via ThreadPool.QueueUserWorkItem will be no more than the cost of starting your own thread, and could be less.

If you just want a few threads for a short period, then use the thread pool. That's what it's for.

Jim Mischel
+10  A: 

The ThreadPool is always there, however, there are a finite number of threads allocated to the pool based on the number of processors. For ASP.NET applications, it is generally a bad idea to use Threads unless you really are starting off an Async process and know that there won't be a large number of requests (remember that there are a finite number of threads in the ThreadPool which you share with everything running in your AppDomain and there is a realistic limit on the total number of threads you want to create using new Thread() as well).

For WinForms apps consider using the BackgroundWorker instead of using a Thread or the ThreadPool. It uses the ThreadPool, however, it makes communicating between threads easier on the multi-thread savvy programmer.

CodeMonkeyKing
A: 

If your tasks are short you will most likely see much better performance by scheduling tasks on the thread pool via QueueUserWorkItem or BeginInvoke as you avoid the repeated cost of creating and destroying your own threads.

The thread pool obviously pays for creating threads as well, but it reuses the threads, so you don't pay the cost per task.

You may also want to take a look at Threading in C# (free ebook).

Brian Rasmussen
A: 

If you're creating your own threads, it makes sense to start them on first use and then let them hang around for a while in case you need them again - you make an idle thread wait on an event so you can reactivate it by signaling the event, and after a long enough time elapses, the thread will wake itself and exit, reclaiming the unused resources. By keeping a small number of idle threads ready to be reused, you reduce the overhead of constantly creating and destroying threads (which is significant for sufficiently short-lived operations).

But this is basically what the thread queue already does for you - so you may as well use it. It's a solved problem.

A similar debate used to happen in the C++ community about strings, believe it or not. Should we each write our own string class, or should we use std::string? The answer depends on whether you want to learn how to write a string class, or you're done with that and you want to get on with inventing something new.

Daniel Earwicker
For the issue you describe, using the asynchronous programming model is much more desireable, and it uses the ThreadPool iirc - and doesn't consume additional resources during the idle time.
SnOrfus
+1  A: 

I was gonna make this a comment, but it got too long.
CodemonkeyKing seems to have hit on an important point, though not strongly enough in my opinion.

There are lots of criteria you might use to describe code. Will it be used in a long running app or not? Winforms app or not? Is it a Server or client app? library or standalone exe? etc etc.

Seems to me that if your code will run in a standalone app and you have control over all the surrounding code, then you can roll your own thread pool, start your own threads, and measure and manage the cost around thread startup, thread latency, and resource consumption. Or you could use the QUWI, but it won't kill your app either way. You are free to choose.

On the other hand if your code is packaged as a library that may be used in a server -in ASP.NET, or maybe in a SQL CLR app, or a WCF Service - then it is a really bad idea to create threads. You need to use QUWI, or some other mechanism that exploits the built-in thread pool (like BackgroundWorker). If it is to be used in client-side apps with other libraries, once again, QUWI is required. Imagine that every library wanting to take advantage of multi-core computers rolled their own threads. There would be complete chaos in apps that used more than a few libraries. Rampant threads, all competing for the same resources. No central coordination of #threads vs # processors.

Good hygeine demands that a library, whether it is to be consumed in client apps or server apps, uses the common threadpool, and that means QUWI.

Last thing to realize is this;

A managed thread is either a background thread or a foreground thread. Background threads are identical to foreground threads with one exception: a background thread does not keep the managed execution environment running. Once all foreground threads have been stopped in a managed process (where the .exe file is a managed assembly), the system stops all background threads and shuts down.

Threads that belong to the managed thread pool (that is, threads whose IsThreadPoolThread property is true) are background threads. All threads that enter the managed execution environment from unmanaged code are marked as background threads. All threads generated by creating and starting a new Thread object are by default foreground threads.

If you use a thread to monitor an activity, such as a socket connection, set its IsBackground property to true so that the thread does not prevent your process from terminating.

from the MSDN site.

Cheeso
A: 

Here's another advantage to using ThreadPool.QueueUserWorkItem.

I have a winforms app which has a hearbeat. I originally implemented this using

            heartbeat = new Thread(HeartbeatDoWork);
        heartbeat.Start();

Which works fine 98% of the time. However when the app is closed sometimes it doesn't 'die' properly, ie the process is still visible in task manager.

To be brief, the heartbeat published an event and it was handling the event (CAB pub/sub) where it was 'getting stuck'.

The fix was easy, just change to using this

            ThreadPool.QueueUserWorkItem(HeartbeatDoWork);

I'm sure there's a few extra things I could do when spinning off my own thread and it to would be cleaned up properly, but this is simpler, faster and easier to understand...the ThreadPool does all the work for me.

wallismark
A: 

One advantage of using Thread.Start() is, that you are able to intentionally abort the thread later. In the ThreadPool you have no means of controlling the thread execution after you enqueued it.

Marcel