BackgroundWorker vs背景线程
我有一个关于在Windows窗体应用程序中使用后台线程实现的select的风格问题。 目前我有一个无限(while(true))
循环窗体上的BackgroundWorker
。 在这个循环中,我使用WaitHandle.WaitAny
来保持线程在打盹,直到感兴趣的事情发生。 我等待的事件句柄之一是一个“ StopThread
”事件,以便我可以跳出循环。 这个事件是从我重写的Form.Dispose()
发出的。
我在某处看到, BackgroundWorker
真的是用于那些你不希望绑定UI的操作,并且有一个有限的结束 – 比如下载一个文件,或者处理一系列的项目。 在这种情况下,“结束”是未知的,只有当窗户closures时。 因此,为了这个目的,使用背景线程而不是BackgroundWorker
会更合适吗?
从我的理解你的问题,你使用一个BackgroundWorker
作为标准的线程。
为什么BackgroundWorker
被推荐用于不想绑定UI线程的原因是因为它在进行Win Forms开发时暴露了一些不错的事件。
像RunWorkerCompleted
这样的事件表示线程何时完成了它所需要做的事情,以及ProgressChanged
事件来更新线程进程上的GUI。
所以,如果你不使用这些,我不觉得使用一个标准的线程有什么害处,你需要做什么。
我的一些想法…
- 如果您有一个单独的任务在后台运行并需要与UI进行交互,请使用BackgroundWorker 。 编组数据和方法调用到UI线程的任务是通过其基于事件的模型自动处理的。 避免BackgroundWorker如果…
- 你的程序集没有或不直接与UI交互,
- 你需要线程成为前台线程,或者
- 你需要操纵线程的优先级。
- 当需要效率时,使用ThreadPool线程。 ThreadPool有助于避免与创build,启动和停止线程相关的开销。 避免使用ThreadPool if …
- 该任务在您的应用程序的整个生命周期中运行
- 你需要线程成为一个前台线程,
- 你需要操纵线程的优先级,或者
- 你需要线程有一个固定的身份(中止,暂停,发现)。
- 将Thread类用于长时间运行的任务,当需要正式线程模型提供的function时,例如在前台和后台线程之间进行select,调整线程优先级,对线程执行的细粒度控制等等。
几乎和Matt Davis所说的一样,还有以下几点:
对于我来说,与BackgroundWorker
的主要区别是通过SynchronizationContext
对已完成事件的自动编组。 在UI上下文中,这意味着已完成的事件在UI线程上触发,因此可用于更新UI。 如果您在UI上下文中使用BackgroundWorker
,这是一个主要区别。
通过ThreadPool
执行的任务不能轻易取消(这包括ThreadPool
, QueueUserWorkItem
和委托执行asynchronous)。 因此,虽然它避免了线程spinup的开销,但是如果您需要取消,可以使用BackgroundWorker
或(更可能在UI之外)旋转线程并保留对其的引用,以便调用Abort()
。
另外,你正在捆绑一个线程池线程的后台工作人员的生命周期,这可能是担心的,因为只有有限的人数。 我会说,如果你只是为你的应用程序创build一次线程(而不使用任何后台工作者的function),那么使用一个线程,而不是一个backgroundworker /线程池线程。
你知道,有时使用BackgroundWorker可以更容易,不pipe你使用的是Windows Forms,WPF还是其他技术。 关于这些家伙的整洁部分是,你可以在线程执行时不用担心太多的线程,这对于简单的任务来说是非常棒的。
在使用BackgroundWorker
之前,首先考虑如果你想取消一个线程(closures应用程序,取消用户),那么你需要确定你的线程是否应该检查取消或者是否应该执行本身。
BackgroundWorker.CancelAsync()
会将CancellationPending
设置为true
但是不会再做任何事情,这是线程的责任,不断检查这一点,还要记住,在用户取消这种方法的情况下,最终会出现竞争条件该线程在testingCancellationPending
之前完成。
另一方面, Thread.Abort()
会在强制取消该线程的线程执行中抛出一个exception,但是如果这个exception在执行过程中突然产生,你必须小心可能会有什么危险。
线程需要非常仔细的考虑,不pipe什么任务,进一步阅读:
并行编程在.NET Framework 托pipe线程的最佳实践
在知道.NET之前,我知道如何使用线程,所以当我开始使用BackgroundWorkers时,已经习惯了一些习惯。 马特·戴维斯(Matt Davis)总结了这个差异,并且非常出色,但是我想补充一点,就是更难理解代码在做什么,这会使debugging更加困难。 考虑创build和closures线程(IMO)比考虑将工作交给一堆线程更容易。
我还是不能评论其他人的post,所以请原谅我用一个答复来解决docker的短暂跛脚7
不要使用Thread.Abort(); 相反,发信号通知一个事件,并devise你的线程优雅地结束。 Thread.Abort()在线程执行的任意点引发一个ThreadAbortExceptionexception,它可以做孤儿监视器,腐败共享状态等各种不愉快的事情。 http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
如果它没有坏 – 修复它,直到它只是开玩笑:)
但是,严肃的BackgroundWorker可能与您已经拥有的非常相似,如果您从一开始就开始使用它,也许您会节省一些时间 – 但在这一点上,我并不认为需要。 除非有什么不工作,或者你认为你现在的代码很难理解,那么我会坚持你所拥有的。
基本的区别就像你说的那样,从BackgroundWorker
生成GUI事件。 如果线程不需要更新显示或为主GUI线程生成事件,那么它可以是一个简单的线程。
后台工作者是一个在单独的线程中工作的类,但是它提供了一些你不能用简单的线程获得的附加function(比如任务进度报告处理)。
如果你不需要后台工作人员提供的附加function – 看起来你没有 – 那么一个线程会更合适。
我想指出一个尚未提及的BackgroundWorker类的一个行为。 通过设置Thread.IsBackground属性,可以使正常的Thread在后台运行。
后台线程与前台线程相同,只是后台线程不阻止进程终止。 [ 1 ]
您可以通过在表单窗口的构造函数中调用以下方法来testing此行为。
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(); }
当IsBackground属性设置为true并closures窗口时,您的应用程序将正常终止。
但是,当IsBackground属性设置为false(默认),并closures窗口,那么只是窗口将消失,但进程仍将继续运行。
BackgroundWorker类使用在后台运行的线程。