BackgroundWorker vs background Thread

2019-01-01 01:34发布

问题:

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?

回答1:

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.



回答2:

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.


回答3:

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().



回答4:

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.



回答5:

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



回答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



回答7:

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.



回答8:

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.



回答9:

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.



回答10:

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 = false; // <--- This will make the thread run in background
    thread.IsBackground = true; // <--- 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.



回答11:

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