后台工作线程 vs 后台线程
我有一个关于我应该在 Windows 窗体应用程序上使用的后台线程实现的选择的风格问题。目前,我的表单上有一个 BackgroundWorker
,它具有无限 (while(true))
循环。在此循环中,我使用 WaitHandle.WaitAny
来保持线程暂停,直到发生感兴趣的事情。我等待的事件句柄之一是“StopThread
”事件,以便我可以跳出循环。当我重写的 Form.Dispose()
时,会发出此事件信号。
我在某处读到,BackgroundWorker
实际上适用于您不想与 UI 绑定且具有有限结束的操作 - 例如下载文件或处理一系列项目。在这种情况下,“结束”是未知的,并且只有当窗口关闭时。因此,对于我来说,使用后台线程而不是 BackgroundWorker 来实现此目的是否更合适?
I have a stylistic question about the choice of background thread implementation I should use on a windows form app. Currently I have a BackgroundWorker
on a form that has an infinite (while(true))
loop. In this loop I use WaitHandle.WaitAny
to keep the thread snoozing until something of interest happens. One of the event handles I wait on is a "StopThread
" event so that I can break out of the loop. This event is signaled when from my overridden Form.Dispose()
.
I read somewhere that BackgroundWorker
is really intended for operations that you don't want to tie up the UI with and have an finite end - like downloading a file, or processing a sequence of items. In this case the "end" is unknown and only when the window is closed. Therefore would it be more appropriate for me to use a background Thread instead of BackgroundWorker
for this purpose?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
我的一些想法...
Some of my thoughts...
根据我对您问题的理解,您正在使用
BackgroundWorker
作为标准线程。之所以推荐使用
BackgroundWorker
来处理您不想占用 UI 线程的事情,是因为它在进行 Win Forms 开发时公开了一些不错的事件。RunWorkerCompleted
等事件用于在线程完成所需操作时发出信号,以及ProgressChanged
事件用于更新有关线程进度的 GUI。因此,如果您没有使用这些,我认为使用标准线程来完成您需要做的事情没有任何坏处。
From my understanding of your question, you are using a
BackgroundWorker
as a standard Thread.The reason why
BackgroundWorker
is recommended for things that you don't want to tie up the UI thread is because it exposes some nice events when doing Win Forms development.Events like
RunWorkerCompleted
to signal when the thread has completed what it needed to do, and theProgressChanged
event to update the GUI on the threads progress.So if you aren't making use of these, I don't see any harm in using a standard Thread for what you need to do.
与 Matt Davis 所说的差不多,但还有以下几点:
对我来说,BackgroundWorker 的主要区别在于通过 SynchronizationContext 自动编组已完成的事件。在 UI 上下文中,这意味着已完成的事件在 UI 线程上触发,因此可用于更新 UI。如果您在 UI 上下文中使用
BackgroundWorker
,这是一个主要的区别。通过
ThreadPool
执行的任务不能轻易取消(这包括ThreadPool
。QueueUserWorkItem
和委托异步执行)。因此,虽然它避免了线程旋转的开销,但如果您需要取消,请使用 BackgroundWorker 或(更可能在 UI 之外)旋转线程并保留对其的引用,以便您可以调用>中止()。Pretty much what Matt Davis said, with the following additional points:
For me the main differentiator with
BackgroundWorker
is the automatic marshalling of the completed event via theSynchronizationContext
. In a UI context this means the completed event fires on the UI thread, and so can be used to update UI. This is a major differentiator if you are using theBackgroundWorker
in a UI context.Tasks executed via the
ThreadPool
cannot be easily cancelled (this includesThreadPool
.QueueUserWorkItem
and delegates execute asyncronously). So whilst it avoids the overhead of thread spinup, if you need cancellation either use aBackgroundWorker
or (more likely outside of the UI) spin up a thread and keep a reference to it so you can callAbort()
.此外,您还需要在后台工作人员的生命周期内占用一个线程池线程,这可能会引起关注,因为它们的数量有限。我想说,如果您只为应用程序创建一次线程(并且不使用后台工作程序的任何功能),那么请使用线程,而不是后台工作程序/线程池线程。
Also you are tying up a threadpool thread for the lifetime of the background worker, which may be of concern as there are only a finite number of them. I would say that if you are only ever creating the thread once for your app (and not using any of the features of background worker) then use a thread, rather than a backgroundworker/threadpool thread.
您知道,有时,无论您使用的是 Windows 窗体、WPF 还是其他技术,使用 BackgroundWorker 都会更容易。这些家伙的巧妙之处在于,您无需过多担心线程的执行位置即可获得线程,这对于简单的任务来说非常有用。
在使用 BackgroundWorker 之前,首先考虑您是否希望取消线程(关闭应用程序、用户取消),然后您需要决定您的线程是否应该检查取消,或者是否应该将其推到执行本身。
BackgroundWorker.CancelAsync()
会将CancellationPending
设置为true
但不会执行任何其他操作,然后线程有责任不断检查这一点,保持还要记住,在这种方法中,您可能会遇到竞争条件,即用户取消,但线程在测试CancellationPending
之前完成。另一方面,Thread.Abort() 会在线程执行中抛出异常,强制取消该线程,但您必须小心,如果在执行中突然引发此异常,可能会产生危险。 。
无论什么任务,线程都需要非常仔细的考虑,请进一步阅读:
并行编程.NET框架
托管线程最佳实践
You know, sometimes it's just easier to work with a BackgroundWorker regardless of if you're using Windows Forms, WPF or whatever technology. The neat part about these guys is you get threading without having to worry too much about where you're thread is executing, which is great for simple tasks.
Before using a
BackgroundWorker
consider first if you wish to cancel a thread (closing app, user cancellation) then you need to decide if your thread should check for cancellations or if it should be thrust upon the execution itself.BackgroundWorker.CancelAsync()
will setCancellationPending
totrue
but won't do anything more, it's then the threads responsibility to continually check this, keep in mind also that you could end up with a race condition in this approach where your user cancelled, but the thread completed prior to testing forCancellationPending
.Thread.Abort()
on the other hand will throw an exception within the thread execution which enforces cancellation of that thread, you must be careful about what might be dangerous if this exception was suddenly raised within the execution though.Threading needs very careful consideration no matter what the task, for some further reading:
Parallel Programming in the .NET Framework
Managed Threading Best Practices
在了解 .NET 之前,我就知道如何使用线程,因此当我开始使用
BackgroundWorker
时,需要一些时间来适应。马特·戴维斯(Matt Davis)非常出色地总结了这种差异,但我想补充一点,准确理解代码的作用更加困难,这会使调试变得更加困难。在我看来,考虑创建和关闭线程比考虑将工作分配给线程池更容易。我仍然无法评论其他人的帖子,所以请原谅我在使用答案来解决 piers7 时的短暂蹩脚
不要使用 Thread.Abort(); 而是使用 Thread.Abort(); 代替,发出事件信号并将线程设计为结束收到信号时优雅地。
Thread.Abort()
在线程执行的任意点引发ThreadAbortException
,这可能会导致各种令人不快的事情,例如孤立监视器、损坏的共享状态等等.http://msdn.microsoft.com/en-我们/library/system.threading.thread.abort.aspx
I knew how to use threads before I knew .NET, so it took some getting used to when I began using
BackgroundWorker
s. Matt Davis has summarized the difference with great excellence, but I would add that it's more difficult to comprehend exactly what the code is doing, and this can make debugging harder. It's easier to think about creating and shutting down threads, IMO, than it is to think about giving work to a pool of threads.I still can't comment other people's posts, so forgive my momentary lameness in using an answer to address piers7
Don't use
Thread.Abort();
instead, signal an event and design your thread to end gracefully when signaled.Thread.Abort()
raises aThreadAbortException
at an arbitrary point in the thread's execution, which can do all kinds of unhappy things like orphan Monitors, corrupt shared state, and so on.http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
我想指出BackgroundWorker 类的一种尚未提及的行为。您可以通过设置 Thread.IsBackground 属性使普通线程在后台运行。
您可以测试此行为通过在表单窗口的构造函数中调用以下方法。
当 IsBackground 属性设置为 true 并且您关闭窗口时,您的应用程序将正常终止。
但是,当 IsBackground 属性设置为 false(默认情况下)并且关闭窗口时,只有窗口会消失,但进程仍会继续运行。
BackgroundWorker 类利用在后台运行的线程。
I want to point out one behavior of BackgroundWorker class that wasn't mentioned yet. You can make a normal Thread to run in background by setting the Thread.IsBackground property.
You can test this behavoir by calling the following method in the constructor of your form window.
When the IsBackground property is set to true and you close the window, then your application will terminate normaly.
But when the IsBackground property is set to false (by default) and you close the window, then just the window will disapear but the process will still keep running.
The BackgroundWorker class utilize a Thread that runs in the background.
如果它没有坏-修复它直到它...只是开玩笑:)
但是说真的,BackgroundWorker可能与你已经拥有的非常相似,如果你从头开始使用它也许你会节省一些时间-但是在这点我认为没有必要。除非某些东西不起作用,或者您认为当前的代码很难理解,否则我会坚持使用您所拥有的代码。
If it ain't broke - fix it till it is...just kidding :)
But seriously BackgroundWorker is probably very similar to what you already have, had you started with it from the beginning maybe you would have saved some time - but at this point I don't see the need. Unless something isn't working, or you think your current code is hard to understand, then I would stick with what you have.
正如您所说,基本区别是从
BackgroundWorker
生成 GUI 事件。如果线程不需要为主 GUI 线程更新显示或生成事件,那么它可以是简单线程。The basic difference is, like you stated, generating GUI events from the
BackgroundWorker
. If the thread does not need to update the display or generate events for the main GUI thread, then it can be a simple thread.后台工作者是一个在单独线程中工作的类,但它提供了简单线程无法获得的附加功能(例如任务进度报告处理)。
如果您不需要后台工作人员提供的附加功能(看起来您不需要),那么线程会更合适。
A background worker is a class that works in a separate thread, but it provides additional functionality that you don't get with a simple Thread (like task progress report handling).
If you don't need the additional features given by a background worker - and it seems you don't - then a Thread would be more appropriate.
如果您在线程内生成异常,它将导致 WinForms 应用程序关闭。由于BackgroundWorker内部生成的异常不会影响应用程序的运行
if you generate exception inside thread it will cause WinForms application to close. With BackgroundWorker generated exceptions inside it will not affect the application running
让我感到困惑的是,视觉工作室设计器只允许您使用实际上并不与服务项目一起使用的BackgroundWorkers和Timers。
它为您提供了整洁的拖放控件到您的服务上,但是......甚至不要尝试部署它。行不通。
服务:
只使用System.Timers.Timer
System.Windows.Forms.Timer 将无法工作,即使它在工具箱
服务中可用:
当作为服务运行时,BackgroundWorkers 将不起作用
使用 System.Threading.ThreadPools 代替或异步调用
What's perplexing to me is that the visual studio designer only allows you to use BackgroundWorkers and Timers that don't actually work with the service project.
It gives you neat drag and drop controls onto your service but... don't even try deploying it. Won't work.
Services:
Only use System.Timers.Timer
System.Windows.Forms.Timer won't work even though it's available in the toolbox
Services:
BackgroundWorkers will not work when it's running as a service
Use System.Threading.ThreadPools instead or Async calls