BackgroundWorker vs背景线程


166

我对应该在Windows Form应用程序上使用的后台线程实现的选择有一个风格上的问题。目前,我BackgroundWorker在具有无限(while(true))循环的表单上。在此循环中,我用于WaitHandle.WaitAny保持线程休眠,直到发生感兴趣的事情为止。我等待的事件句柄之一是“ StopThread”事件,因此我可以跳出循环。从我重写时发出此事件信号Form.Dispose()

我读过某个地方,该地方BackgroundWorker实际上是您不希望将UI与之捆绑在一起并具有有限结局的操作-例如下载文件或处理一系列项目。在这种情况下,“结束”是未知的,只有在关闭窗口时才知道。因此,对我而言,使用后台线程而不是BackgroundWorker为此目的更合适吗?

Answers:


88

根据我对您问题的理解,您正在使用a BackgroundWorker作为标准线程。

BackgroundWorker建议对您不想占用UI线程的事物进行推荐的原因是,它在进行Win Forms开发时会暴露一些不错的事件。

事件喜欢RunWorkerCompleted在线程完成所需操作时发出信号,而在线程上ProgressChanged更新GUI 的事件正在进行中。

因此,如果您使用这些线程,那么对于需要执行的操作使用标准线程不会有任何危害。


我不确定的另一个问题是,假设我正在尝试处置运行有背景工作人员的表格。我发出了shutdownevent(ManualResetEvent)信号,此后的某个时间DoWork将正常退出。即使DoWork可能需要花费更长的时间来完成,还是应该有某种方法(更好)进行线程处理,我是否应该让表单继续进行处理并加入Dispose。的形式继续吗?
freddy smith,2009年

我认为BackgroundWorker.IsBusy是您在那儿寻找的东西。
ParmesanCodice

1
仅使用CancelAsync(并测试CancellationPending是否在短时间内轮询线程,如果您想System.Threading.Thread.Abort()引发异常,则使用a 在线程块本身内引发异常,为情况选择正确的模型。)
Brett Ryan

369

我的一些想法...

  1. 使用BackgroundWorker如果您有一个任务在后台运行并且需要与UI交互,。通过基于事件的模型自动处理将数据和方法调用编组到UI线程的任务。如果出现以下情况,请避免使用BackgroundWorker:
    • 您的程序集没有U​​I或没有直接与UI交互,
    • 您需要将该线程作为前台线程,或者
    • 您需要操纵线程优先级。
  2. 使用线程池需要效率时,线程。ThreadPool有助于避免与创建,启动和停止线程相关的开销。避免在以下情况下使用ThreadPool:
    • 该任务将在您的应用程序的整个生命周期内运行,
    • 您需要将该线程作为前台线程,
    • 您需要操纵线程优先级,或者
    • 您需要线程具有固定的身份(中止,暂停,发现)。
  3. Thread类用于长时间运行的任务,并且在需要正式线程模型提供的功能时,例如在前台线程和后台线程之间进行选择,调整线程优先级,对线程执行的细粒度控制等时,请使用Thread类。

10
后台工作程序位于System.dll程序集和System.ComponentModel命名空间中。没有依赖于Winforms。
Kugel

17
没错,但是BackgroundWorker旨在将线程进度报告给感兴趣的一方,通常涉及UI。该类的MSDN文档对此非常清楚。如果您只需要在后台执行任务,则最好使用ThreadPool线程。
Matt Davis

5
关于您关于System.Windows.Forms组装的观点;BackgroundWorker对WPF应用程序也很有用,并且这些应用程序可能未引用WinForms。
GiddyUpHorsey 2011年

12

马特·戴维斯(Matt Davis)讲的差不多,还有以下几点:

对我而言,主要的区别BackgroundWorker是通过SynchronizationContext。在UI上下文中,这意味着Completed事件会在UI线程上触发,因此可用于更新UI。如果BackgroundWorker在UI上下文中使用,则这是一个主要区别。

通过ThreadPool取消执行的任务不能轻易取消(包括ThreadPoolQueueUserWorkItem和委托异步执行)。因此,虽然它避免了线程启动的开销,但是如果您需要取消操作,请使用BackgroundWorker或(更可能在UI之外)启动线程并保留对其的引用,以便可以调用Abort()


1
仅...希望该应用程序设计用于停止线程任务的干净方法(通常不是终止)

11

另外,您还要在后台工作程序的生命周期内绑定线程池线程,这可能是一个令人担忧的问题,因为它们数量有限。我想说的是,如果您只为应用程序创建一次线程(并且不使用后台工作程序的任何功能),那么请使用线程,而不是后台工作程序/线程池线程。


1
我认为这是一个好点。因此,如果您在Form的生命周期中需要“临时”后台线程,那么我从中得到的消息是使用后台线程,但是如果您在表单的整个生命周期(可能是几分钟,几小时,天...),然后使用Thread代替BackgroundWorker,以免滥用ThreadPool的目的
freddy smith

关于:“ ...可能会引起关注,因为它们的数量有限”,您是否表示操作系统上的其他应用可能需要它们并从同一“池”中共享?
丹·W

8

您知道,有时,无论您使用的是Windows Forms,WPF还是其他技术,使用BackgroundWorker都更容易。这些家伙的妙处在于,您不必担心线程在哪里执行就可以进行线程处理,这对于简单的任务非常有用。

使用前BackgroundWorker先考虑,如果你想取消线程(关闭应用程序,用户取消),那么你需要决定是否你的线程应该检查取消,或者如果它应该是在执行本身的推力。

BackgroundWorker.CancelAsync()将设置CancellationPending为,true但将不再执行任何操作,那么线程责任将继续检查这一点,并牢记您可能会在这种情况下最终陷入竞争状态,即用户取消了该线程,但是在测试之前该线程已完成CancellationPending

Thread.Abort() 另一方面,它将在线程执行中引发强制取消该线程的异常,但是您必须注意,如果在执行过程中突然引发此异常可能会造成危险。

无论执行什么任务,线程都需要非常仔细的考虑,以便进一步阅读:

.NET Framework中的并行编程 托管线程最佳实践


5

我在知道.NET之前就知道如何使用线程,因此在开始使用BackgroundWorkers 时花了一些时间来适应。马特·戴维斯(Matt Davis)非常出色地总结了它们之间的区别,但是我要补充一点,要准确地理解代码的作用更加困难,这会使调试变得更加困难。考虑到创建和关闭线程(IMO)要比考虑给线程池工作要容易得多。

我仍然无法评论其他人的帖子,因此请原谅我一时的me脚,使用答案来回答码头问题

不要使用,Thread.Abort();而是通知一个事件,并设计您的线程以在收到信号时优雅地结束。 在线程执行的任意点Thread.Abort()引发a ThreadAbortException,它可以执行各种不快乐的事情,例如孤立监视器,损坏的共享状态等等。
http://msdn.microsoft.com/zh-CN/library/system.threading.thread.abort.aspx


2

如果它还没坏-修复它直到...只是在开玩笑:)

但是认真地来说,BackgroundWorker可能与您已经拥有的非常相似,如果您从一开始就开始使用它,也许您就可以节省一些时间-但目前我认为没有必要。除非某些问题不起作用,或者您认为当前的代码难以理解,否则我会坚持使用现有的代码。


2

如您所述,基本的区别是从中生成GUI事件BackgroundWorker。如果该线程不需要更新主GUI线程的显示或生成事件,则它可以是一个简单的线程。


2

我想指出一个尚未提及的BackgroundWorker类行为。您可以通过设置Thread.IsBackground属性来使普通线程在后台运行。

后台线程与前台线程相同,只是后台线程不会阻止进程终止。[ 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 = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

当IsBackground属性设置为true并关闭窗口时,应用程序将正常终止。

但是,当IsBackground属性设置为false(默认情况下)并关闭窗口时,仅该窗口将消失,但该过程仍将继续运行。

BackgroundWorker类利用在后台运行的Thread。


1

后台工作程序是在单独的线程中工作的类,但是它提供了其他简单的线程无法获得的其他功能(例如任务进度报告处理)。

如果您不需要后台工作人员提供的其他功能(似乎不需要),那么使用Thread更为合适。


-1

让我感到困惑的是,Visual Studio设计器只允许您使用实际上与服务项目不兼容的BackgroundWorkers和Timers。

它为您提供了整洁的拖放控件到您的服务上,但是...甚至不要尝试部署它。不行

服务:仅使用System.Timers.Timer System.Windows.Forms.Timer即使在工具箱中也无法使用

服务:BackgroundWorkers作为服务运行时将无法工作,请改用System.Threading.ThreadPools或Async调用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.