Click here to Skip to main content
11,435,048 members (49,535 online)
Click here to Skip to main content

Tagged as

Background Thread? Let me count the ways....

, 14 Aug 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Ten fun ways of doing something on a background thread

Introduction

This article (well more of a ditty really) demonstrates ten (previously nine) fabulous ways of doing something on a background thread in C#. It serves little point other than providing something to talk about in the pub when no-one interesting is around.

Background

I was once asked in an interview to describe different ways of doing something on a background thread. They wanted two answers but I gave them three. This was in 2008, and there were probably four ways then – create a new thread and use that, use the ThreadPool, use the Asynchronous Programming Model (APM) via a delegate or use a timer. I suppose the BackgroundWorker was around too, so let's say five then, but no self-respecting person would mention that.

Someone has kindly pointed out that I missed another way which would be to spawn a second process which would have its own threads and do the work there. This doesn't quite fit the criteria of a background thread in my mind, but *is* a way of doing something rather clumsily in the background. So, let's say six!

But time has moved on, and they love re-inventing things so we now have the Task Parallel Library (TPL), Parallel LINQ (PLINQ) and even the Reactive Framework (RX) which can all be coerced into doing a background operation. (If you want to run the RX approach in the example source, you’ll need to get the RX package from NuGet.)

Pretty much every approach ends up assigning the operation to the ThreadPool, they are just different ways of getting it there. Without further ado, here are ten ways of doing something on a background thread. I'm sure there are more, so feel free to advise me of those I've missed and call me an idiot. Also, please note that I've got to write the code and article before my wife gets back from coffee with her friends as we've got to go to Blue Water (a shopping centre in Kent) to buy pants, so won’t have time to check that anything claimed here is correct. Sorry.

The Long Term Commitment

The most obvious way of doing something in the background is to create a thread and do it on that. But, this is discouraged for small operations as it’s an expensive process. Also, a thread typically comes furnished with 1MB stack so creating the things willy-nilly is going to hurt your memory consumption. This approach is best when the background operation is going to be long running:

public void LongTermCommitment()
{
    // create a thread, execute the background method and block until it's done
    Thread thread = new Thread(_ => BackgroundMethod("Long Term Commitment"));
    thread.Start();
    thread.Join();
}

The Gentleman's Approach

Because thread creation is expensive .NET comes with a thread pool which is like a nice home for threads. They are kept locked up and only let out to do something then put back in again, thus avoiding lots of creation and destruction each time.

There’s plenty of information about the ThreadPool all over the internet so let’s not mention it further. I know it, you know it and my dog knows it:

public void TheGentlemansApproach()
{
    // straight onto the threadpool - what could be better?
    ThreadPool.QueueUserWorkItem(_ => BackgroundMethod("The Gentleman's Approach"));
}

The Mentalist

Ah yes, the Asynchronous Programming Model or APM. This has been around since the early days but largely ignored in every bit of code I’ve seen. Is it any wonder? It involves something called an IAsyncResult and that sounds horrible. To do this, simply call BeginInvoke on a delegate and it fires off on the ThreadPool. You then need to call EndInvoke which will block your current thread until the delegate returns. I seem to recall that you must do this or something horrible happens, but can’t remember what:

public void TheMentalist()
{
    // Use the Asyncronous Programming Model (APM) - a bit ugly in my eyes
    BackgroundMethodDelegate x = new BackgroundMethodDelegate(BackgroundMethod);
    IAsyncResult a = x.BeginInvoke("The Mentalist", null, null);
    x.EndInvoke(a);
}

The VB Sissy

Let’s face it; anyone who programs in VB is weak. And although I can’t prove it I suspect when they invented the BackgroundWorker they had the weak in mind. This is for people who need to do something in the background so they don’t lock up their GUI but don’t really get threading, so it has nice methods that post back to the calling thread updates. This is very much of the old fashioned form of using a message loop, so when you call it from a Console App like we are doing here that isn't going to work:

public void TheVbSissy()
{
    // BackgroundWorkers are for wimps. Case closed.
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += delegate { BackgroundMethod("The VB Sissy Approach"); };
    worker.RunWorkerAsync();
}

Someone asked below is there a valid reason not to like the BackgroundWoker other than VB snobbery. This is a fair question and the answer is no, not really. As alluded to above, this is for use when developing a GUI so that any long running or blocking operations don't lock up the user interface. In fact, if you need to do a long-running operation such as loading a large file into memory it is quite a good choice. It will do the work on a background thread and has built in support for reporting back progress and completion.

The golden rule in GUI programming is that only the thread that creates a control can update it, because the message-loop that is used is itself not thread-safe, and the BackgroundWorker takes care of the thread switching for you. I have never used this thing myself, instead favouring creating encapsulated methods on the form which BeginInvoke onto the GUI thread, and thus are thread-safe. Multiple threads can then go bananas as much as they like.

That's how I do it, but each to their own as they say. If you don't want to do that, then go ahead and use the BackgroundWorker. (You sissy)

The Insane Co-Worker

We all have them, and after all why put something directly on the ThreadPool when you can create a timer that fires once, immediately and do the operation in that. Things like this do happen and when I see them usually have to leave the building for a short while to contemplate the failure of mankind whilst choking on a Marlboro Light. Do not do this:

public void TheInsaneCoWorker()
{
    // this requires a certain level of teeth gritting
    Timer timer = new Timer(_ => BackgroundMethod("The Insane Coworker"), null, 0, Timeout.Infinite);
}

For clarity, I am using the System.Threading.Timer here. There is also a System.Timers.Timer which could be used. Both WinForms and WPF also provide different timers, but these post back to the main thread rather than executing on a background thread so cannot be used.

The Task At Hand

Onto TPL, which provides a layer of abstraction over the ThreadPool. This approach embodies the operation in a thing called a Task, which provides richer functionality and control, providing nice features such as cancellation. It’s simple and looks like this:

public void TheTaskAtHand()
{
    // first of many TPL ways
    using (Task task = new Task(() => BackgroundMethod("The Task At Hand")))
    {
        task.Start();
        task.Wait();
    }
}

The Task in The Other Hand

Or, looks like this:

public void TheTaskInOtherHand()
{
    Task.Factory.StartNew(() => BackgroundMethod("The Task In The Other Hand"));
}

The Contemporary Approach

This is quite neat really. We now have a very straight-forward way of invoking something in the background:

public void TheContemporaryApproach()
{
    // pretty neat - one has to admit
    Parallel.Invoke(() => BackgroundMethod("The Contemporary Approach"));
}

The Show Off

I've been fiddling around with RX recently, and it’s essentially LINQ having undergone a sex-change. Things which were pulled are now pushed, and you can set up subscribers which receive the pushed item on a background thread. So here we push the item to a subscriber which handles it, and the thread-switching is all done seamlessly. If you haven’t played around with RX yet, it’s pretty cool once you get your head around the essential idea:

public void TheShowOff()
{
    // RX - push the item into a subscribed method
    Observable.Return("The Show Off", Scheduler.Default).Subscribe(BackgroundMethod);
}

The Second Process

Here's one I didn't think of but has been suggested as a way of doing something in the background, even if it doesn't qualify as a background thread. (By definition, a background thread is a thread in a process which will not prevent the process from terminating while it is still running.)

In this article I have been getting background threads to output a string directly to the console. A second process could do this, but to its console not ours. So, it's a bit feeble but we'll get it to do that and collect the string from the second process standard output stream and display it. Hence it will be output on the main thread but will have been supplied by a second thread in a separate process.

This really is a brute force approach, and one doesn't want to get into the realms of inter-process communication to do something as trivial as this in the background. That said, it's not uncommon to shell out to a separate worker process to get things done but I think defining this as a 'background process' would be better.

To make this work, I have added a switch in our app, so that if a command line argument is supplied it will just output it to the console, otherwise it will do all the different approaches as before. We can then do this:

public void OutOfProcess()
{
    // this is a bit of a kludge as we are writing to the console on the main application thread
    // even so, the operation (writing a string to output) is done in a second process and hence
    // thread, but we have to redirect the output here to see it....
    ProcessStartInfo startInfo = new ProcessStartInfo("StartThreads.exe", "OutOfProcess");
    startInfo.CreateNoWindow = false;
    startInfo.UseShellExecute = false;
    startInfo.RedirectStandardOutput = true;

    Process process = Process.Start(startInfo);
    Console.WriteLine("Approach \"{0}\" sent from second process",
        process.StandardOutput.ReadToEnd());
    process.WaitForExit();
}

Conclusion

You are given a fair amount of choice about how to do things these days and hopefully this demonstrates that point. Personally I think there’s a little too much choice and that leads people to do the same thing in different ways which in turn complicates the development cycle a bit. I suppose we should obey and just use TPL for everything and think of the older ways as obsolete and there for backward compatibility.

Points of Interest

Well, I can’t see any, sadly.

History

  • 8th August 2013: Initial version
  • 13th August 2013: Update to include second process - thanks to thelazydogsback for suggesting this.
  • 14th August 2013: Toned down about BackgroundWorker, sort of - thanks to Mike Cattle for pointing out my code bigotry.

License

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

Share

About the Author

Rob Philpott
Architect
United Kingdom United Kingdom
I am a .NET architect/developer based in London working mostly on financial trading systems. My love of computers started at an early age with BASIC on a 3KB VIC20 and progressed onto a 32KB BBC Micro using BASIC and 6502 assembly language. From there I moved on to the blisteringly fast Acorn Archimedes using BASIC and ARM assembly.

I started developing with C++ since 1990, where it was introduced to me in my first year studying for a Computer Science degree at the University of Nottingham. I started professionally with Visual C++ version 1.51 in 1993.

I moved over to C# and .NET in early 2004 after a long period of denial that anything could improve upon C++.

Recently I did a bit of work in my old language of C++ and I now realise that frankly, it's a total pain in the arse.

Comments and Discussions

 
Questiondodgy pub talk! Pin
PMcB18-Nov-14 5:27
memberPMcB18-Nov-14 5:27 
QuestionFive! Pin
Brady Kelly2-Nov-14 2:52
memberBrady Kelly2-Nov-14 2:52 
GeneralMy vote of 5 Pin
jamicore16-Jul-14 13:01
memberjamicore16-Jul-14 13:01 
GeneralMixed feelings Pin
Ivaylo5ev24-Oct-13 22:37
memberIvaylo5ev24-Oct-13 22:37 
GeneralMy vote of 5 Pin
smatveev4-Oct-13 4:31
membersmatveev4-Oct-13 4:31 
GeneralMy vote of 5 Pin
Todd Pichler24-Sep-13 3:01
memberTodd Pichler24-Sep-13 3:01 
GeneralMy vote of 1 Pin
Ben Burnett23-Sep-13 19:44
memberBen Burnett23-Sep-13 19:44 
GeneralRe: My vote of 1 Pin
Rob Philpott23-Sep-13 22:53
professionalRob Philpott23-Sep-13 22:53 
Question.Net 4.5 Pin
Member 1028993623-Sep-13 11:46
memberMember 1028993623-Sep-13 11:46 
AnswerRe: .Net 4.5 Pin
Rob Philpott23-Sep-13 22:56
professionalRob Philpott23-Sep-13 22:56 
GeneralMy vote of 5 Pin
Qwertie23-Sep-13 11:43
memberQwertie23-Sep-13 11:43 
QuestionRunning a console app as a true background process... Pin
KRucker23-Sep-13 10:51
memberKRucker23-Sep-13 10:51 
AnswerRe: Running a console app as a true background process... Pin
Rob Philpott23-Sep-13 11:21
professionalRob Philpott23-Sep-13 11:21 
GeneralMy vote of 5 Pin
vlad_pol@hotmail.com23-Sep-13 9:25
membervlad_pol@hotmail.com23-Sep-13 9:25 
GeneralRe: My vote of 5 Pin
Rob Philpott23-Sep-13 22:56
professionalRob Philpott23-Sep-13 22:56 
QuestionThreading seems Threatening, seeking advice as a Thread beginner Pin
Babak Sekandari23-Sep-13 9:23
memberBabak Sekandari23-Sep-13 9:23 
AnswerRe: Threading seems Threatening, seeking advice as a Thread beginner Pin
Rob Philpott23-Sep-13 11:18
professionalRob Philpott23-Sep-13 11:18 
QuestionTo VB || ! to VB Pin
Member 904013723-Sep-13 8:13
memberMember 904013723-Sep-13 8:13 
GeneralMy vote of 2 Pin
MahBulgaria23-Sep-13 3:44
memberMahBulgaria23-Sep-13 3:44 
GeneralMy vote of 5 Pin
Singyuen Yip22-Sep-13 22:08
memberSingyuen Yip22-Sep-13 22:08 
GeneralMy vote of 3 Pin
Paulo Zemek17-Sep-13 17:39
professionalPaulo Zemek17-Sep-13 17:39 
GeneralRe: My vote of 3 Pin
Rob Philpott21-Sep-13 23:20
professionalRob Philpott21-Sep-13 23:20 
GeneralRe: My vote of 3 [modified] Pin
Paulo Zemek22-Sep-13 5:06
professionalPaulo Zemek22-Sep-13 5:06 
GeneralMy vote of 5 Pin
Renju Vinod16-Sep-13 19:57
professionalRenju Vinod16-Sep-13 19:57 
GeneralMy vote of 5 Pin
kounadg11-Sep-13 21:37
professionalkounadg11-Sep-13 21:37 
GeneralMy vote of 2 Pin
Athari10-Sep-13 22:38
professionalAthari10-Sep-13 22:38 
GeneralMy vote of 5 Pin
BillWoodruff8-Sep-13 3:30
memberBillWoodruff8-Sep-13 3:30 
QuestionGreat article Pin
Milan Stanacev6-Sep-13 23:30
memberMilan Stanacev6-Sep-13 23:30 
GeneralMy vote of 5 Pin
Hamed Mosavi6-Sep-13 22:37
memberHamed Mosavi6-Sep-13 22:37 
GeneralMy vote of 5 Pin
valery possoz6-Sep-13 13:04
membervalery possoz6-Sep-13 13:04 
GeneralRe: My vote of 5 Pin
Rob Philpott6-Sep-13 20:49
professionalRob Philpott6-Sep-13 20:49 
QuestionOne issue [modified] Pin
FatCatProgrammer6-Sep-13 5:35
memberFatCatProgrammer6-Sep-13 5:35 
AnswerRe: One issue Pin
Paulo Zemek7-Sep-13 14:14
professionalPaulo Zemek7-Sep-13 14:14 
GeneralMy vote of 4 Pin
Sivaji15656-Sep-13 1:57
memberSivaji15656-Sep-13 1:57 
GeneralMy vote of 5 Pin
Peter Hayward20-Aug-13 16:25
memberPeter Hayward20-Aug-13 16:25 
QuestionMy Vote of 5 Pin
Kenneth Salter19-Aug-13 12:17
professionalKenneth Salter19-Aug-13 12:17 
GeneralThread.IsBackground Pin
KevinAG19-Aug-13 9:22
memberKevinAG19-Aug-13 9:22 
GeneralRe: Thread.IsBackground Pin
Rob Philpott19-Aug-13 9:37
professionalRob Philpott19-Aug-13 9:37 
QuestionI love this one Pin
Sacha Barber19-Aug-13 5:37
mvpSacha Barber19-Aug-13 5:37 
AnswerRe: I love this one Pin
Rob Philpott19-Aug-13 19:53
professionalRob Philpott19-Aug-13 19:53 
GeneralRe: I love this one Pin
Sacha Barber21-Aug-13 0:42
mvpSacha Barber21-Aug-13 0:42 
QuestionWhat about async and await? Pin
Rohit_Vats17-Aug-13 5:57
memberRohit_Vats17-Aug-13 5:57 
AnswerRe: What about async and await? Pin
Rob Philpott17-Aug-13 6:05
professionalRob Philpott17-Aug-13 6:05 
GeneralRe: What about async and await? Pin
Rohit_Vats17-Aug-13 6:09
memberRohit_Vats17-Aug-13 6:09 
GeneralMy vote of 4 Pin
balvino16-Aug-13 5:56
memberbalvino16-Aug-13 5:56 
SuggestionMy 5 and Other ways Pin
Chris at IAGAM15-Aug-13 15:02
memberChris at IAGAM15-Aug-13 15:02 
GeneralRe: My 5 and Other ways Pin
Rob Philpott15-Aug-13 19:43
professionalRob Philpott15-Aug-13 19:43 
GeneralRe: My 5 and Other ways Pin
Chris at IAGAM15-Aug-13 19:56
memberChris at IAGAM15-Aug-13 19:56 
GeneralMy vote of 5 Pin
Monjurul Habib15-Aug-13 9:09
professionalMonjurul Habib15-Aug-13 9:09 
Questionnice one, thanks Pin
AmitMukherjee15-Aug-13 7:35
memberAmitMukherjee15-Aug-13 7:35 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150428.2 | Last Updated 14 Aug 2013
Article Copyright 2013 by Rob Philpott
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid