C#:我是否需要处理在运行时创建的BackgroundWorker?

时间:2009-11-18 12:18:14

标签: c# backgroundworker dispose

我通常在表单上有这样的代码:

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

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

        worker.RunWorkerAsync();
    }

这意味着我没有处置BackgroundWorker,而如果我已经由表单设计者添加它,那么我认为它会被处理掉。

这会导致任何问题吗?声明模块级_saveWorker,然后从表单的dispose方法调用Dispose更正确吗?

8 个答案:

答案 0 :(得分:32)

是的,你应该处理后台工作人员。

您可能会发现使用ThreadPool.QueueUserWorkItem(...)更容易,之后不需要任何清理。


有关为何应始终调用Dispose()的其他详细信息:

虽然如果查看BackgroundWorker类,它实际上并没有在它的dispose方法中进行任何线程清理,但由于该类对垃圾收集器的影响,调用Dispose仍然很重要。

具有终结器的类不会立即进行GC。它们被保留并添加到终结器队列中。然后运行终结器线程(遵循标准模式调用dispose)。这意味着该对象将存在于GC生成1中。而第1代集合远比gen 0集合少,因此您可以在内存中反复使用更长时间。

但是如果你调用Dispose(),对象将不会被添加到终结队列中,因此可以自由地进行垃圾回收。

这不是一个很大的问题,但如果你创造了很多这样的问题,你最终会使用比必要更多的内存。在对具有dispose方法的对象上调用dispose应该被认为是一种好的做法。

所以我想,总而言之,这不是100%的硬性和快速要求。如果您不调用Dispose(),您的应用程序将不会爆炸(甚至泄漏内存),但在某些情况下,它可能会产生负面影响。后台工作程序设计为可以作为WinForms组件使用,所以如果你有不同的要求,并且不想将它用作WinForms组件,请不要使用它,使用正确的工具,喜欢ThreadPool。

答案 1 :(得分:16)

面临的挑战是确保只有在完成后才能处理 。您无法在BackgroundWorker事件中执行此操作,因为该事件由BackgroundWorker本身引发。

BackgroundWorker真的打算用作WinForms表单上的一个组件,所以我建议你这样做,或者切换到Completed之类的东西。这将使用线程池线程,并且在完成时不需要任何特殊的清理。

答案 2 :(得分:7)

在我看来,一般情况下,如果它是IDisposable,那么当你完成它时它应该是Dispose()d。即使当前的BackgroundWorker实现在技术上不需要处理,您也不希望后来的内部实现感到惊讶。

答案 3 :(得分:6)

我不会打扰,Bgw可以保留的唯一资源是Thread,如果你的代表中没有无限循环那么你就没事了。

BackgroundWorker从Component继承IDisposable()但并不真正需要它。

将它与直接在ThreadPool上推送方法进行比较。你没有(不能)处理线程,当然不是池中的线程。

但是如果你的样本是完整的,你没有使用Completed事件或Progress / Cancel功能,你也可以使用ThreadPool.QueueUserWorkItem()

答案 4 :(得分:3)

为什么不用using语句换行?没有多少额外的努力,你得到了处置:

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

编辑:

好的,我把一个小小的测试放在一起,看看有什么处理和处理:

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);
        }
    }
}

这是输出:

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

从某些测试中,似乎Dispose()对启动的BackgroundWorker基本没有影响。无论你是否在using语句的范围内调用它,或者使用它在代码中声明并立即处理它并取消引用它,它仍然可以正常运行。 Disposed事件发生在主线程上,DoWork和RunWorkerCompleted发生在线程池线程上(事件触发时可用的任何一个)。我尝试了一个案例,我在调用Dispose之后立即取消注册RunWorkerCompleted事件(所以在DoWork有机会完成之前)并且RunWorkerCompleted没有激活。这让我相信尽管处理了BackgroundWorker对象,你仍然可以操作它。

正如其他人所提到的那样,此时看起来似乎并不是真的需要调用Dispose。但是,我也没有看到任何伤害,至少从我的经验和这些测试来看。

答案 5 :(得分:3)

在您的RunWorkerCompleted活动中致电处理。

BackgroundWorker wkr = new BackgroundWorker();
wkr.DoWork += (s, e) => {
    // Do long running task.
};
wkr.RunWorkerCompleted += (s, e) => {
    try {
        if (e.Error != null) {
            // Handle failure.
        }
    } finally {
        // Use wkr outer instead of casting.
        wkr.Dispose();
    }
};
wkr.RunWorkerAsync();

额外的try / finally是为了确保在完成代码引发异常时调用Dispose

答案 6 :(得分:2)

为所有IDisposable对象调用Dispose()被认为是最佳实践。这允许他们释放他们可能持有的非托管资源,例如Handles。 IDisisableable类也应该有终结器,其存在可以延迟GC被允许完全收集这些对象的时间。

如果你的类分配了一个IDisposable并将它分配给一个成员变量,那么一般来说它本身也应该是IDisposable。

但是,如果不调用Dispose(),最终将调用Finalizer,最终将清理资源。明确调用它的好处是,这些事情可以更快地发生并且开销更少,从而提高应用程序性能并减少内存压力。

答案 7 :(得分:0)

完成处理程序在原始线程上运行(即,不是线程池中的后台线程)!您的测试结果实际上证实了这一前提。