views:

2051

answers:

6

I typically have code like this on a form:

    private void PerformLongRunningOperation()
    {
        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += delegate
        {
            // perform long running operation here
        };

        worker.RunWorkerAsync();
    }

This means that I don't dispose the BackgroundWorker, whereas if I had added it by the form designer then I think it would get disposed.

Will this cause any problems? Is it more correct to declare a module-level _saveWorker, and then call Dispose on it from the form's dispose?

+3  A: 

I wouldn't bother, the only resource the Bgw could hold on to is the Thread and if you don't have an infinite loop in your delegate then you are fine.

BackgroundWorker inherits IDisposable() from Component, but doesn't really need it.

Compare it to pushing your method directly on the ThreadPool. You don't (can't) Dispose threads, certaily not those from the Pool.

But if your sample is complete in the sense that you are not using the Completed event or Progress/Cancel features you could just as well use ThreadPool.QueueUserWorkItem().

Henk Holterman
Thread objects are very expensive (they, at least, consists of large reserved stack memory, maybe 1MB).
denisenkom
Yes, they are expensive. But how would you clean them up? Besides, the Bgw uses the ThreadPool.
Henk Holterman
There are at least 2 good reasons for calling dispose on the BGW. firstly it calls GC.SuppressFinalize(), and secondly because of the point that Wayne makes about future implementations and not being reliant on internal implementation details.
Simon P Stevens
You're probably right about SupressFinalize, if you really worry about that then add it to Form.components. I wouldn't take the future argument too far, it is legacy from Component .
Henk Holterman
+6  A: 

Yes, you should dispose of the background worker.

You may find it easier to use ThreadPool.QueueUserWorkItem(...) which doesn't require any clean up afterwards.


Additional detail on why you should always call Dispose():

Although if you look in the BackgroundWorker class it doesn't actually do any thread clean up in it's dispose method, it is still important to call Dispose because of the effect the class has on the garbage collector.

Classes with finalizers are not GCed immediately. They are kept and added to the finalizer queue. The finalizer thread then runs, (which following the standard pattern calls dispose). This means the object will survive into GC generation 1. And gen 1 collections are far rarer than gen 0 collections, so you object sticks around in memory for much longer.

If however you call Dispose(), the object will not be added to the finalization queue, so is free to be garbage collected.

It's not really big problem, but if you are creating a lot of them you'll end up using more memory than necessary. It should really be considered good practise to always call dispose on objects that have a dispose method.

So I suppose, all in all, it's not a 100% hard and fast requirement. Your app won't explode (or even leak memory) if you don't call Dispose(), but in some circumstances it may have negative effects. The background worker was designed for use from as a WinForms component so use it that way, if you have different requirements and don't want to use it as a WinForms component, don't use it, use the correct tool for the job, like the ThreadPool.

Simon P Stevens
_Why_ should he call bgw.Dispose()? Besides the usual 'because it's there' logic.
Henk Holterman
@Henk. I've added some extra detail. It's not 100% critical, but it was designed to be called. If you don't want to call it there are other classes that are a better fit for non-Winforms code.
Simon P Stevens
+6  A: 

The challenge is making sure you only dispose the BackgroundWorker after it's finished running. You can't do that in the Completed event, because that event is raised by the BackgroundWorker itself.

BackgroundWorker is really intended to be used as a component on a WinForms form, so I would recommend either doing that, or switching to something like Thread.QueueUserWorkItem. This will use a thread-pool thread, and won't require any special cleanup when it's finished.

Neil Barnwell
+1 For `BackgroundWorker` being intended to be used as a WinForms component.
Brian
+1  A: 

It's considered a best practice to call Dispose() for all IDisposable objects. That allows them to release unmanaged resources they may be holding, such as Handles. IDisposable classes also should have finalizers, whose presence can delay the time at which the GC is allowed to fully collect those objects.

If your class allocates an IDisposable and assigns it to a member variable, then in general it should itself also be IDisposable.

However, if you don't call Dispose(), then the Finalizer will eventually be called, and the resources will eventually be cleaned up. The advantage of calling it explicitly is that those things can happen more quickly and with less overhead, which improve app performance and reduce memory pressure.

RickNZ
"IDisposable classes also should have finalizers, whose presence can delay the time at which the GC is allowed to fully collect those objects." <- that is true only if the IDisposable has *un*managed resources to clean up
dss539
I suggest that an object usually should only implement IDisposable if it's dealing with unmanaged resources; otherwise the GC does all the work (perhaps except in cases where you'd like to be able to leverage the semantics of the "using" statement). Also, IDisposable objects often reference other IDiposables, so they many not hold unmanaged resources directly, but rather indirectly.
RickNZ
+5  A: 

In my view, in general, if it's IDisposable, it should be Dispose()d when you're done with it. Even if the current implementation of BackgroundWorker doesn't technically need to be disposed, you don't want to be surprised by later internal implementations that might.

Wayne
I think that is a very good point about future implementations. Yes, the current BGW doesn't do anything in it's dispose method, but there is no way to guarantee the .net 4.0 BGW won't do some clean up in there.
Simon P Stevens
That sounds good but the truth is that Bgw.Dispose is just legacy from it's base class.
Henk Holterman
@henk it is **for now**. MS make no guarantee than one day it won't be, which is **exactly** why they recommend always calling `Dispose()` on things that implement `IDisposable`.
Neil Barnwell
A: 

Why not wrap in a using statement? Not much extra effort and you get the disposal:

private void PerformLongRunningOperation()
    {
        using (BackgroundWorker worker = new BackgroundWorker())
        {
            worker.DoWork += delegate
                             {
                                 // perform long running operation here 
                             };
            worker.RunWorkerAsync();
        }
    }

EDIT:

Ok, I put together a little test to kinda see what is going on with disposal and whatnot:

using System;
using System.ComponentModel;
using System.Threading;

namespace BackgroundWorkerTest
{
    internal class Program
    {
        private static BackgroundWorker _privateWorker;

        private static void Main()
        {
            PrintThread("Main");
            _privateWorker = new BackgroundWorker();
            _privateWorker.DoWork += WorkerDoWork;
            _privateWorker.RunWorkerCompleted += WorkerRunWorkerCompleted;
            _privateWorker.Disposed += WorkerDisposed;
            _privateWorker.RunWorkerAsync();
            _privateWorker.Dispose();
            _privateWorker = null;

            using (var BW = new BackgroundWorker())
            {
                BW.DoWork += delegate
                                 {
                                     Thread.Sleep(2000);
                                     PrintThread("Using Worker Working");
                                 };
                BW.Disposed += delegate { PrintThread("Using Worker Disposed"); };
                BW.RunWorkerCompleted += delegate { PrintThread("Using Worker Completed"); };
                BW.RunWorkerAsync();
            }

            Console.ReadLine();
        }

        private static void WorkerDisposed(object sender, EventArgs e)
        {
            PrintThread("Private Worker Disposed");
        }

        private static void WorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            PrintThread("Private Worker Completed");
        }

        private static void WorkerDoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(2000);
            PrintThread("Private Worker Working");
        }

        private static void PrintThread(string caller)
        {
            Console.WriteLine("{0} Thread: {1}", caller, Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Here is the output:

Main Thread: 1
Private Worker Disposed Thread: 1
Using Worker Disposed Thread: 1
Private Worker Working Thread: 3
Using Worker Working Thread: 4
Using Worker Completed Thread: 4
Private Worker Completed Thread: 3

From some testing, it appears that Dispose() has basically no effect on an initiated BackgroundWorker. Whether or not you call it in the scope of a using statement, or use it declared in code and immediately dispose it and dereference it, it still runs normally. The Disposed Event occurs on the main thread, and the DoWork and RunWorkerCompleted occur on thread pool threads (whichever one is available when the event fires). I tried a case where I unregistered the RunWorkerCompleted event right after I called Dispose (so before DoWork had a chance to complete) and RunWorkerCompleted did not fire. This leads me to believe that you can still manipulate the BackgroundWorker object despite it being disposed.

So as others have mentioned, at this time, it does appear as though calling Dispose is not really required. However, I do not see any harm in doing it either, at least from my experience and these tests.

jomtois
Does this work? Won't it Dispose of the worker too early?
RickL
I have used this many times and it definitely "works" -- it does not kill off the background process too early. However, I have never found a definitive answer anywhere if this is really the right thing to do or if it truly does the disposal. I was hoping someone that knows for sure would weigh in on that.
jomtois
I added a message box in the Disposed event of the BackgroundWorker, and the message box showed after the async operation was completed. To me, it appears the the BackgroundWorker is disposing of itself after it completes its work. Do you agree with that assessment?
joek1975