我有一个关于我应该在Windows窗体应用程序上使用的后台线程实现的选择的风格问题。目前,我在表单上有BackgroundWorker
,其中包含无限(while(true))
个循环。在这个循环中,我使用WaitHandle.WaitAny
来保持线程打盹直到感兴趣的事情发生。我等待的一个事件句柄是“StopThread
”事件,这样我就可以摆脱循环。来自我被覆盖的Form.Dispose()
时会发出此事件的信号。
我在某处读到BackgroundWorker
实际上是用于那些你不想将UI绑定并具有有限结束的操作 - 比如下载文件或处理一系列项目。在这种情况下,“结束”是未知的,并且仅在窗口关闭时。因此,为此目的使用后台线程而不是BackgroundWorker
会更合适吗?
答案 0 :(得分:350)
我的一些想法......
答案 1 :(得分:83)
根据我对您的问题的理解,您使用BackgroundWorker
作为标准线程。
为不希望绑定UI线程的事情推荐BackgroundWorker
的原因是因为它在进行Win Forms开发时会暴露一些不错的事件。
像RunWorkerCompleted
这样的事件在线程完成它需要做的事情时发出信号,并在ProgressChanged
事件中更新线程上的GUI进度。
因此,如果你没有使用这些,我认为使用标准线程不会对你需要做什么造成任何伤害。
答案 2 :(得分:12)
Matt Davis说的话几乎有以下几点:
对我来说,BackgroundWorker
的主要区别是通过SynchronizationContext
自动编组完成的事件。在UI上下文中,这意味着已完成的事件在UI线程上触发,因此可用于更新UI。如果您在UI上下文中使用BackgroundWorker
,这是一个主要的区别。
通过ThreadPool
执行的任务无法轻松取消(包括ThreadPool
。QueueUserWorkItem
和委托执行异步)。因此,虽然它避免了线程旋转的开销,但是如果你需要取消,可以使用BackgroundWorker
或(更可能在UI之外)启动一个线程并保持对它的引用,这样你就可以调用Abort()
答案 3 :(得分:10)
此外,您在后台工作程序的生命周期中绑定了一个线程池线程,这可能会引起关注,因为它们只有有限数量。我会说,如果你只是为你的应用程序创建一次线程(而不是使用后台工作程序的任何功能),那么使用一个线程,而不是一个backgroundworker / threadpool线程。
答案 4 :(得分:8)
您知道,无论您使用的是Windows窗体,WPF还是其他任何技术,有时使用BackgroundWorker都会更容易。关于这些人的简洁部分是你得到线程而不必过多担心线程执行的位置,这对于简单的任务来说非常有用。
在使用BackgroundWorker
之前,如果您想要取消线程(关闭应用程序,用户取消),那么您需要确定您的线程是否应检查取消或是否应该在执行本身上进行检查。
BackgroundWorker.CancelAsync()
会将CancellationPending
设置为true
,但不会再执行任何操作,因此线程有责任不断检查此项,请记住您最终可能会这种方法中的竞争条件,用户取消了,但在测试CancellationPending
之前完成了线程。
Thread.Abort()
将在线程执行中抛出一个异常,强制取消该线程,如果在执行过程中突然引发了这个异常,你必须小心可能会有什么危险。
无论任务是什么,线程都需要非常仔细地考虑,以便进一步阅读:
Parallel Programming in the .NET Framework Managed Threading Best Practices
答案 5 :(得分:4)
在我熟悉.NET之前,我知道如何使用线程,因此在我开始使用BackgroundWorkers时需要一些时间来习惯。 Matt Davis总结了这种差异,并且非常出色,但我想补充说,理解代码正在做什么更难以理解,这可能会使调试变得更加困难。考虑创建和关闭线程IMO比考虑为线程池提供工作更容易。
我仍然无法评论其他人的帖子,所以原谅我在使用答案解决码头时的短暂跛脚7
不要使用Thread.Abort();相反,发出一个事件信号并设计你的线程在发出信号时优雅地结束。 Thread.Abort()在线程执行中的任意点引发ThreadAbortException,它可以执行各种不愉快的事情,如孤立监视器,损坏的共享状态等等。 http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
答案 6 :(得分:2)
如果它没有破损 - 修复它直到它......开玩笑:)
但是认真的BackgroundWorker可能与你已经拥有的非常相似,如果你从一开始就开始使用它,也许你会节省一些时间 - 但是在这一点上我没有看到需要。除非某些东西不起作用,或者你认为你当前的代码很难理解,那么我会坚持你所拥有的。
答案 7 :(得分:2)
与您所述的基本区别在于,从BackgroundWorker
生成GUI事件。如果线程不需要更新显示或为主GUI线程生成事件,那么它可以是一个简单的线程。
答案 8 :(得分:1)
后台工作程序是一个在单独的线程中工作的类,但它提供了一个简单的线程无法获得的附加功能(如任务进度报告处理)。
如果你不需要后台工作者提供的附加功能 - 而且你似乎没有 - 那么一个Thread会更合适。
答案 9 :(得分:1)
我想指出尚未提及的BackgroundWorker类的一个行为。您可以通过设置Thread.IsBackground属性使正常的Thread在后台运行。
后台线程与前台线程相同,但后台线程不会阻止进程终止。 [1]
您可以通过在窗体窗口的构造函数中调用以下方法来测试此行为。
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并关闭窗口时,您的应用程序将正常终止。
但是当IsBackground属性设置为false(默认情况下)并关闭窗口时,只有窗口会消失,但进程仍将继续运行。
BackgroundWorker类使用在后台运行的Thread。
答案 10 :(得分:-1)
令我感到困惑的是,视觉工作室设计师只允许您使用不实际使用服务项目的BackgroundWorkers和Timers。
它为您提供整洁的拖放控件,但是......甚至不尝试部署它。不会工作。
服务: 仅使用System.Timers.Timer System.Windows.Forms.Timer即使在工具箱中可用也不会工作
服务: BackgroundWorkers在作为服务运行时将无法工作 请改用System.Threading.ThreadPools或Async calls