183

I have a stylistic question about the choice of background thread implementation I should use on a windows form app. Currently I have a BackgroundWorker on a form that has an infinite (while(true)) loop. In this loop I use WaitHandle.WaitAny to keep the thread snoozing until something of interest happens. One of the event handles I wait on is a "StopThread" event so that I can break out of the loop. This event is signaled when from my overridden Form.Dispose().

I read somewhere that BackgroundWorker is really intended for operations that you don't want to tie up the UI with and have an finite end - like downloading a file, or processing a sequence of items. In this case the "end" is unknown and only when the window is closed. Therefore would it be more appropriate for me to use a background Thread instead of BackgroundWorker for this purpose?

Naser Asadi
  • 1,153
  • 18
  • 35
freddy smith
  • 3,347
  • 5
  • 24
  • 28

12 Answers12

389

Some of my thoughts...

  1. Use BackgroundWorker if you have a single task that runs in the background and needs to interact with the UI. The task of marshalling data and method calls to the UI thread are handled automatically through its event-based model. Avoid BackgroundWorker if...
    • your assembly does not have or does not interact directly with the UI,
    • you need the thread to be a foreground thread, or
    • you need to manipulate the thread priority.
  2. Use a ThreadPool thread when efficiency is desired. The ThreadPool helps avoid the overhead associated with creating, starting, and stopping threads. Avoid using the ThreadPool if...
    • the task runs for the lifetime of your application,
    • you need the thread to be a foreground thread,
    • you need to manipulate the thread priority, or
    • you need the thread to have a fixed identity (aborting, suspending, discovering).
  3. Use the Thread class for long-running tasks and when you require features offered by a formal threading model, e.g., choosing between foreground and background threads, tweaking the thread priority, fine-grained control over thread execution, etc.
Matt Davis
  • 45,297
  • 16
  • 93
  • 124
  • 10
    Background worker is in the System.dll assembly and System.ComponentModel namespace. There is no dependency on Winforms. – Kugel Jan 31 '11 at 17:36
  • 17
    That is correct, but `BackgroundWorker` is designed to report thread progress to an interested party, which usually involves a UI. The MSDN documentation for the class makes this abundantly clear. If you simply need a task to be performed in the background, prefer using a `ThreadPool` thread. – Matt Davis Jan 31 '11 at 18:04
  • 5
    With regards to your point about the `System.Windows.Forms` assembly; `BackgroundWorker` is also useful for WPF apps too and those apps may not have a reference to WinForms. – John Mills Sep 22 '11 at 05:43
  • In this case, for a "background" thread that lives with the UI application, it is wiser to use BackgroundWorker unless I desire the formal Thread model? – The_Matrix Aug 17 '23 at 17:38
  • 1
    @The_Matrix Just because you have a UI application doesn't necessarily mean you should use `BackgroudWorker` for any necessary threads. Limit use of `BackgroundWorker` to those cases where the thread needs to interact with the UI directly, e.g., update a progress bar. Otherwise, prefer one of the other two models. Short-lived threads should use the ThreadPool, which today means using the Task class. Threads the need to live for the duration of the application's lifetime should probably use the Thread class directly. – Matt Davis Aug 17 '23 at 19:36
95

From my understanding of your question, you are using a BackgroundWorker as a standard Thread.

The reason why BackgroundWorker is recommended for things that you don't want to tie up the UI thread is because it exposes some nice events when doing Win Forms development.

Events like RunWorkerCompleted to signal when the thread has completed what it needed to do, and the ProgressChanged event to update the GUI on the threads progress.

So if you aren't making use of these, I don't see any harm in using a standard Thread for what you need to do.

Naser Asadi
  • 1,153
  • 18
  • 35
ParmesanCodice
  • 5,017
  • 1
  • 23
  • 20
  • one other issue I am unsure of is, suppose I am trying to dispose the form that has the backgroundworker running on it. I signal the shutdownevent (ManualResetEvent) and sometime after that the DoWork will gracefully exit. Should I just let the form go ahead and Dispose even though the DoWork might take a little longer to finish, or is there some way (and is it better) to thread.Join the background worker until it really does exit and then let the Dispose of the form continue? – freddy smith Oct 01 '09 at 23:46
  • I think BackgroundWorker.IsBusy is what you are looking for there. – ParmesanCodice Oct 02 '09 at 06:37
  • 1
    Only use `CancelAsync` (and test for `CancellationPending` if your thread will be polling on short intervals, if you want to have an exception raised instead, use a `System.Threading.Thread.Abort()` which raises an exception within the thread block itself, choose the right model for the situation. – Brett Ryan Apr 21 '11 at 10:19
12

Pretty much what Matt Davis said, with the following additional points:

For me the main differentiator with BackgroundWorker is the automatic marshalling of the completed event via the SynchronizationContext. In a UI context this means the completed event fires on the UI thread, and so can be used to update UI. This is a major differentiator if you are using the BackgroundWorker in a UI context.

Tasks executed via the ThreadPool cannot be easily cancelled (this includes ThreadPool. QueueUserWorkItem and delegates execute asyncronously). So whilst it avoids the overhead of thread spinup, if you need cancellation either use a BackgroundWorker or (more likely outside of the UI) spin up a thread and keep a reference to it so you can call Abort().

Conrad
  • 2,197
  • 28
  • 53
piers7
  • 4,174
  • 34
  • 47
  • 1
    Only ... hopefully the application is designed about a *clean* method of stopping a threaded task (Abort is generally not it) –  Mar 19 '11 at 22:21
11

Also you are tying up a threadpool thread for the lifetime of the background worker, which may be of concern as there are only a finite number of them. I would say that if you are only ever creating the thread once for your app (and not using any of the features of background worker) then use a thread, rather than a backgroundworker/threadpool thread.

Matt
  • 2,984
  • 1
  • 24
  • 31
  • 1
    I think this is a good point. So the message I take from this is use a background worker if you need a background thread "temporarily" during the lifetime of the Form, however if you need a background thread for the entire lifetime of the form (which could be minutes, hours, days...) then use a Thread instead of BackgroundWorker so as to not misuse the purpose of the ThreadPool – freddy smith Oct 02 '09 at 00:49
  • Regarding: "...which may be of concern as there are only a finite number of them", do you mean other apps on the OS may need them and share from the same 'pool' ? – Dan W May 09 '16 at 08:47
10

You know, sometimes it's just easier to work with a BackgroundWorker regardless of if you're using Windows Forms, WPF or whatever technology. The neat part about these guys is you get threading without having to worry too much about where you're thread is executing, which is great for simple tasks.

Before using a BackgroundWorker consider first if you wish to cancel a thread (closing app, user cancellation) then you need to decide if your thread should check for cancellations or if it should be thrust upon the execution itself.

BackgroundWorker.CancelAsync() will set CancellationPending to true but won't do anything more, it's then the threads responsibility to continually check this, keep in mind also that you could end up with a race condition in this approach where your user cancelled, but the thread completed prior to testing for CancellationPending.

Thread.Abort() on the other hand will throw an exception within the thread execution which enforces cancellation of that thread, you must be careful about what might be dangerous if this exception was suddenly raised within the execution though.

Threading needs very careful consideration no matter what the task, for some further reading:

Parallel Programming in the .NET Framework Managed Threading Best Practices

Brett Ryan
  • 26,937
  • 30
  • 128
  • 163
6

I knew how to use threads before I knew .NET, so it took some getting used to when I began using BackgroundWorkers. Matt Davis has summarized the difference with great excellence, but I would add that it's more difficult to comprehend exactly what the code is doing, and this can make debugging harder. It's easier to think about creating and shutting down threads, IMO, than it is to think about giving work to a pool of threads.

I still can't comment other people's posts, so forgive my momentary lameness in using an answer to address piers7

Don't use Thread.Abort(); instead, signal an event and design your thread to end gracefully when signaled. Thread.Abort() raises a ThreadAbortException at an arbitrary point in the thread's execution, which can do all kinds of unhappy things like orphan Monitors, corrupt shared state, and so on.
http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

Yousha Aleayoub
  • 4,532
  • 4
  • 53
  • 64
ajs410
  • 2,384
  • 2
  • 19
  • 14
5

I want to point out one behavior of BackgroundWorker class that wasn't mentioned yet. You can make a normal Thread to run in background by setting the Thread.IsBackground property.

Background threads are identical to foreground threads, except that background threads do not prevent a process from terminating. [1]

You can test this behavoir by calling the following method in the constructor of your form window.

void TestBackgroundThread()
{
    var thread = new Thread((ThreadStart)delegate()
    {
        long count = 0;
        while (true)
        {
            count++;
            Debug.WriteLine("Thread loop count: " + count);
        }
    });

    // Choose one option:
    thread.IsBackground = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

When the IsBackground property is set to true and you close the window, then your application will terminate normaly.

But when the IsBackground property is set to false (by default) and you close the window, then just the window will disapear but the process will still keep running.

The BackgroundWorker class utilize a Thread that runs in the background.

Doomjunky
  • 1,148
  • 17
  • 18
2

If it ain't broke - fix it till it is...just kidding :)

But seriously BackgroundWorker is probably very similar to what you already have, had you started with it from the beginning maybe you would have saved some time - but at this point I don't see the need. Unless something isn't working, or you think your current code is hard to understand, then I would stick with what you have.

Gandalf
  • 9,648
  • 8
  • 53
  • 88
2

The basic difference is, like you stated, generating GUI events from the BackgroundWorker. If the thread does not need to update the display or generate events for the main GUI thread, then it can be a simple thread.

David R Tribble
  • 11,918
  • 5
  • 42
  • 52
1

A background worker is a class that works in a separate thread, but it provides additional functionality that you don't get with a simple Thread (like task progress report handling).

If you don't need the additional features given by a background worker - and it seems you don't - then a Thread would be more appropriate.

Cesar
  • 5,488
  • 2
  • 29
  • 36
0

if you generate exception inside thread it will cause WinForms application to close. With BackgroundWorker generated exceptions inside it will not affect the application running

-1

What's perplexing to me is that the visual studio designer only allows you to use BackgroundWorkers and Timers that don't actually work with the service project.

It gives you neat drag and drop controls onto your service but... don't even try deploying it. Won't work.

Services: Only use System.Timers.Timer System.Windows.Forms.Timer won't work even though it's available in the toolbox

Services: BackgroundWorkers will not work when it's running as a service Use System.Threading.ThreadPools instead or Async calls