112

I see a lot of people in blog posts and here on SO either avoiding or advising against the usage of the Thread class in recent versions of C# (and I mean of course 4.0+, with the addition of Task & friends). Even before, there were debates about the fact that a plain old thread's functionality can be replaced in many cases by the ThreadPool class.

Also, other specialized mechanisms are further rendering the Thread class less appealing, such as Timers replacing the ugly Thread + Sleep combo, while for GUIs we have BackgroundWorker, etc.

Still, the Thread seems to remain a very familiar concept for some people (myself included), people that, when confronted with a task that involves some kind of parallel execution, jump directly to using the good old Thread class. I've been wondering lately if it's time to amend my ways.

So my question is, are there any cases when it's necessary or useful to use a plain old Thread object instead of one of the above constructs?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Tudor
  • 61,523
  • 12
  • 102
  • 142

10 Answers10

107

The Thread class cannot be made obsolete because obviously it is an implementation detail of all those other patterns you mention.

But that's not really your question; your question is

are there any cases when it's necessary or useful to use a plain old Thread object instead of one of the above constructs?

Sure. In precisely those cases where one of the higher-level constructs does not meet your needs.

My advice is that if you find yourself in a situation where existing higher-abstraction tools do not meet your needs, and you wish to implement a solution using threads, then you should identify the missing abstraction that you really need, and then implement that abstraction using threads, and then use the abstraction.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 34
    All good advice. But do you have an example where a plain old thread object might be preferable to one of the newer constructs? – Robert Harvey Mar 27 '12 at 23:35
  • Debugging multiple threads with timing issues to load some code can be done a lot easier at times then using other 'higher' level constructs. And you need the basic knowledge of working and using Threads anyway. – CodingBarfield Mar 28 '12 at 08:00
  • Thanks for the answer, Eric. It's really easy to forget, under the recent bombardment about the new threading features, that sometimes the old thread is still needed. Well, I guess knowledge of the bare-metal is useful to have anyway. – Tudor Mar 28 '12 at 09:57
  • 15
    @RobertHarvey: Sure. For example, if you have a classic "producer/consumer" situation, where you want to have one thread dedicated to taking stuff out of a work queue and another thread dedicated to putting stuff into a work queue. Both loop forever; they don't map nicely to tasks that you do for a while and then get a result. You don't need a thread pool because there are only two threads. And you can be clever with your use of locks and signals to ensure that the queue is kept safe without blocking the other thread for a long time. – Eric Lippert Mar 28 '12 at 14:54
  • @Eric: Aren't such abstraction already offered by [TPL Dataflow](http://msdn.microsoft.com/en-us/devlabs/gg585582)? – Allon Guralnek Apr 03 '12 at 17:54
52

Threads are a basic building block for certain things (namely parallelism and asynchrony) and thus should not be taken away. However, for most people and most use cases there are more appropriate things to use which you mentioned, such as thread pools (which provide a nice way of handling many small jobs in parallel without overloading the machine by spawning 2000 threads at once), BackgroundWorker (which encapsulates useful events for a single shortlived piece of work).

But just because in many cases those are more appropriate as they shield the programmer from needlessly reinventing the wheel, doing stupid mistakes and the like, that does not mean that the Thread class is obsolete. It is still used by the abstractions named above and you would still need it if you need fine-grained control over threads that is not covered by the more special classes.

In a similar vein, .NET doesn't forbid the use of arrays, despite List<T> being a better fit for many cases where people use arrays. Simply because you may still want to build things that are not covered by the standard lib.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Joey
  • 344,408
  • 85
  • 689
  • 683
  • I'm currently failing to think of a suitable analogy. – Joey Mar 27 '12 at 17:53
  • 6
    Just because an automatic transmission works 99% of the time, doesn't mean there is no value in manual transmissions, even ignoring driver preference. – Guvante Mar 27 '12 at 17:56
  • 4
    Not very familiar with driving idioms given that I'm a cyclist and public transport user. But a manual transmission isn't at the heart of an automatic one – it's not a mechanical hand moving a stick. It's not really an abstraction over the other, as far as I can see it. – Joey Mar 27 '12 at 17:57
  • 2
    You could replace the word "thread" with "unmanaged code" in the second paragraph and it will still ring true – Conrad Frix Mar 27 '12 at 17:57
  • 1
    @Joey: Oh, I thought you meant the obsolescence piece, the value of having fine grained control, at the cost of usability, even though most will never need it. BTW technically the interesting part of a manual transmission is the clutch anyway. – Guvante Mar 27 '12 at 18:02
  • @Guvante: I would opine that the interesting difference between a manual and an automatic transmission is not the clutch; a manual transmission has a foot-operated clutch and an automatic transmission has a computer-operated clutch. The interesting difference in my mind is that an automatic transmission selects the gear ratio via a *planetary* gear. – Eric Lippert Mar 27 '12 at 18:27
  • @EricLippert: I was going from an interface or abstraction viewpoint. An automatic requires two petals, while a manual requires three. While the gear selection interface also changes, that is a pretty obvious change to most users. – Guvante Mar 27 '12 at 19:05
  • 3
    If you guys really want to go with the transmission metaphor, most sequential transmissions (such as BMW's SMG transmission) *are*, in fact, manual transmissions with a computer operated clutch and gear selector. They aren't planetary gear systems like conventional automatics. – Adam Robinson Mar 27 '12 at 22:04
  • @AdamRobinson: I did not know that! How interesting. – Eric Lippert Mar 28 '12 at 16:59
  • 1
    @Eric and many now add in double clutch systems so that the change from one gear to the next can, in effect be somewhat parallelised... and thus the circle of metaphor is complete! – ShuggyCoUk Mar 28 '12 at 17:16
13

Task and Thread are different abstractions. If you want to model a thread, the Thread class is still the most appropriate choice. E.g. if you need to interact with the current thread, I don't see any better types for this.

However, as you point out .NET has added several dedicated abstractions which are preferable over Thread in many cases.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
9

The Thread class is not obsolete, it is still useful in special circumstances.

Where I work we wrote a 'background processor' as part of a content management system: a Windows service that monitors directories, e-mail addresses and RSS feeds, and every time something new shows up execute a task on it - typically to import the data.

Attempts to use the thread pool for this did not work: it tries to execute too much stuff at the same time and trash the disks, so we implemented our own polling and execution system using directly the Thread class.

MiMo
  • 11,793
  • 1
  • 33
  • 48
  • I have no idea whether using Thread directly here is the right solution here, but +1 for actually giving an example where going to Thread was required. – Oddthinking Mar 28 '12 at 00:06
6

The new options make direct use and management of the (expensive) threads less frequent.

people that, when confronted with a task that involves some kind of parallel execution, jump directly to using the good old Thread class.

Which is a very expensive and relatively complex way of doing stuff in parallel.

Note that the expense matters most: You cannot use a full thread to do a small job, it would be counterproductive. The ThreadPool combats the costs, the Task class the complexities (exceptions, waiting and canceling).

H H
  • 263,252
  • 30
  • 330
  • 514
5

To answer the question of "are there any cases when it's necessary or useful to use a plain old Thread object", I'd say a plain old Thread is useful (but not necessary) when you have a long running process that you won't ever interact with from a different thread.

For example, if you're writing an application that subscribes to receive messages from some sort of message queue and you're application is going to do more than just process those messages then it would be useful to use a Thread because the thread will be self-contained (i.e. you aren't waiting on it to get done), and it isn't short-lived. Using the ThreadPool class is more for queuing up a bunch of short-lived work items and allowing the ThreadPool class manage efficiently processing each one as a new Thread is available. Tasks can be used where you would use Thread directly, but in the above scenario I don't think they would buy you much. They help you interact with the thread more easily (which the above scenario doesn't need) and they help determine how many Threads actually should be used for the given set of tasks based on the number of processors you have (which isn't what you want, so you'd tell the Task your thing is LongRunning in which case in the current 4.0 implementation it would simply create a separate non-pooled Thread).

Derek Greer
  • 15,454
  • 5
  • 45
  • 52
  • I agree that most of the cool new parallelism things are designed around short-term small fine-grained tasks, and long running threads should still be implemented with `System.IO.Threading.Thread`. – Ben Voigt Mar 28 '12 at 00:54
4

Probably not the answer you were expecting, but I use Thread all the time when coding against the .NET Micro Framework. MF is quite cut down and doesn't include higher level abstractions and the Thread class is super flexible when you need to get the last bit of performance out of a low MHz CPU.

AngryHacker
  • 59,598
  • 102
  • 325
  • 594
  • Hey, that's actually a pretty good example! Thanks. I haven't thought about frameworks that don't support the new features. – Tudor Mar 29 '12 at 08:59
3

You could compare the Thread class to ADO.NET. It's not the recommended tool for getting the job done, but its not obsolete. Other tools build on top of it to ease the job.

Its not wrong to use the Thread class over other things, especially if those things don't provide a functionality that you need.

Kyle Trauberman
  • 25,414
  • 13
  • 85
  • 121
3

It's not definitely obsolete.

The problem with multithreaded apps is that they are very hard to get right (often indeterministic behavior, input, output and also internal state is important), so a programmer should push as much work as possible to framework/tools. Abstract it away. But, the mortal enemy of abstraction is performance.

So my question is, are there any cases when it's necessary or useful to use a plain old Thread object instead of one of the above constructs?

I'd go with Threads and locks only if there will be serious performance problems, high performance goals.

Lukasz Madon
  • 14,664
  • 14
  • 64
  • 108
3

I've always used the Thread class when I need to keep count and control over the threads I've spun up. I realize I could use the threadpool to hold all of my outstanding work, but I've never found a good way to keep track of how much work is currently being done or what the status is.

Instead, I create a collection and place the threads in them after I spin them up - the very last thing a thread does is remove itself from the collection. That way, I can always tell how many threads are running, and I can use the collection to ask each what it's doing. If there's a case when I need to kill them all, normally you'd have to set some kind of "Abort" flag in your application, wait for every thread to notice that on its own and self-terminate - in my case, I can walk the collection and issue a Thread.Abort to each one in turn.

In that case, I haven't found a better way that working directly with the Thread class. As Eric Lippert mentioned, the others are just higher-level abstractions, and it's appropriate to work with the lower-level classes when the available high-level implementations don't meet your need. Just as you sometimes need to do Win32 API calls when .NET doesn't address your exact needs, there will always be cases where the Thread class is the best choice despite recent "advancements."

SqlRyan
  • 33,116
  • 33
  • 114
  • 199