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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1755597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 20:32:33  来源:igfitidea点击:

C#: Do I need to dispose a BackgroundWorker created at runtime?

c#backgroundworkerdispose

提问by RickL

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.

这意味着我不会处理BackgroundWorker,而如果我是由表单设计者添加的,那么我认为它会被处理。

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

这会导致任何问题吗?声明一个 module-level _saveWorker,然后Dispose从表单的 dispose 方法中调用它是否更正确?

采纳答案by Simon P Stevens

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.

您可能会发现它更易于使用ThreadPool.QueueUserWorkItem(...),之后无需进行任何清理。



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

关于为什么应该始终调用 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.

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

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.

带有终结器的类不会立即被 GC。它们被保留并添加到终结器队列中。然后终结器线程运行(遵循标准模式调用 dispose)。这意味着对象将存活到 GC 第 1 代。第 1 代的集合比第 0 代的集合少得多,因此您的对象在内存中停留的时间要长得多。

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

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

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.

这不是什么大问题,但如果你创建了很多它们,你最终会使用比必要更多的内存。始终对具有 dispose 方法的对象调用 dispose 确实应该被认为是一种很好的做法。

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.

所以我想,总而言之,这不是一个 100% 的硬性要求。如果您不调用 Dispose(),您的应用程序不会爆炸(甚至泄漏内存),但在某些情况下它可能会产生负面影响。后台工作程序被设计为用作 WinForms 组件,因此请以这种方式使用它,如果您有不同的要求并且不想将其用作 WinForms 组件,请不要使用它,使用正确的工具来完成工作,就像线程池一样。

回答by Henk Holterman

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.

我不会打扰,Bgw 可以保留的唯一资源是线程,如果您的委托中没有无限循环,那么您很好。

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

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

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

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

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

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

回答by Neil Barnwell

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

挑战在于确保您只BackgroundWorker它完成运行处理它。您不能在Completed事件中这样做,因为该事件是由 BackgroundWorker 本身引发的。

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.

BackgroundWorker 真的打算用作 WinForms 表单上的组件,所以我建议要么这样做,要么切换到类似Thread.QueueUserWorkItem. 这将使用线程池线程,完成后不需要任何特殊清理。

回答by RickNZ

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.

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

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

如果您的类分配了一个 IDisposable 并将其分配给一个成员变量,那么通常它本身也应该是 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.

但是,如果不调用Dispose(),那么最终会调用Finalizer,最终会清理资源。显式调用它的优点是这些事情可以更快地发生,开销更少,从而提高应用程序性能并减少内存压力。

回答by lesscode

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.

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

回答by Jon Comtois

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

为什么不包含在 using 语句中?没有太多额外的努力,你就可以得到处置:

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.

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

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.

因此,正如其他人所提到的,此时似乎确实不需要调用 Dispose。但是,至少从我的经验和这些测试来看,我也不认为这样做有任何害处。

回答by Brett Ryan

Call dispose in your RunWorkerCompletedevent.

在您的RunWorkerCompleted事件中调用 dispose 。

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

The extra try/finally is to ensure that Disposegets called if your completion code raises an exception.

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

回答by John Richards

The completion handler is run on the original thread (i.e., not the background thread from the thread pool)! Your test results actually confirm that premise.

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