我通常在表单上有这样的代码:
private void PerformLongRunningOperation()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
// perform long running operation here
};
worker.RunWorkerAsync();
}
这意味着我没有处置BackgroundWorker
,而如果我已经由表单设计者添加它,那么我认为它会被处理掉。
这会导致任何问题吗?声明模块级_saveWorker
,然后从表单的dispose方法调用Dispose
更正确吗?
答案 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)
完成处理程序在原始线程上运行(即,不是线程池中的后台线程)!您的测试结果实际上证实了这一前提。