BackgroundWorker与后台线程

fre*_*ith 163 .net multithreading backgroundworker winforms

我有一个关于我应该在Windows窗体应用程序上使用的后台线程实现的选择的风格问题.目前我BackgroundWorker在一个具有无限(while(true))循环的表单上.在这个循环中,我WaitHandle.WaitAny用来保持线程打盹直到感兴趣的事情发生.我等待的一个事件句柄是一个" StopThread"事件,这样我就可以摆脱循环.来自我被覆盖的事件会发出此事件的信号Form.Dispose().

我读到的某个地方BackgroundWorker真的是用于那些你不想将UI绑定到并且具有有限结束的操作 - 比如下载文件或处理一系列项目.在这种情况下,"结束"是未知的,并且仅在窗口关闭时.因此,使用后台线程而不是BackgroundWorker为此目的更合适吗?

Mat*_*vis 361

我的一些想法......

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

  • 这是正确的,但`BackgroundWorker`旨在向感兴趣的一方报告线程进度,这通常涉及UI.该课程的MSDN文档非常清楚.如果您只需要在后台执行任务,则更喜欢使用`ThreadPool`线程. (17认同)
  • 后台工作程序位于System.dll程序集和System.ComponentModel命名空间中.Winforms没有任何依赖性. (9认同)
  • 关于你对`System.Windows.Forms`程序集的看法; `BackgroundWorker`对WPF应用程序也很有用,这些应用程序可能没有WinForms的引用. (5认同)

Par*_*ice 87

根据我对您的问题的理解,您使用的BackgroundWorker是标准线程.

为什么原因BackgroundWorker建议的事情,你不想占用UI线程是因为它做赢形式的发展时,暴露了一些不错的活动.

事件喜欢RunWorkerCompleted在线程完成它需要做的事情时发出信号,以及ProgressChanged在线程进度上更新GUI 的事件.

因此,如果您没有使用这些,我认为使用标准线程不会对您需要做什么造成任何伤害.


pie*_*rs7 12

几乎是马特戴维斯所说的,还有以下几点:

对我来说,主要区别BackgroundWorker在于通过自动编组已完成的事件SynchronizationContext.在UI上下文中,这意味着已完成的事件在UI线程上触发,因此可用于更新UI.如果您BackgroundWorker在UI上下文中使用,这是一个主要的区别.

通过它执行的任务ThreadPool不能轻易取消(包括ThreadPool.QueueUserWorkItem和委托执行异步).因此,虽然它避免了线程旋转的开销,但是如果你需要取消使用BackgroundWorker或者(更可能在UI之外)启动一个线程并保持对它的引用,这样你就可以调用Abort().


Mat*_*att 11

你也在为后台工作者的生命周期绑定一个线程池线程,这可能会引起关注,因为它们只有有限数量.我会说,如果你只是为你的应用程序创建一次线程(而不是使用后台工作程序的任何功能),那么使用一个线程,而不是一个backgroundworker/threadpool线程.


Bre*_*yan 8

您知道,无论您使用的是Windows窗体,WPF还是其他任何技术,有时使用BackgroundWorker都会更容易.关于这些人的整洁部分是你得到线程,而不必过多担心线程执行的位置,这对于简单的任务非常有用.

在使用BackgroundWorker考虑之前,如果你想取消一个线程(关闭应用程序,用户取消),那么你需要决定你的线程是否应检查取消或是否应该强制执行本身.

BackgroundWorker.CancelAsync()将设置CancellationPendingtrue但不会再执行任何操作,然后线程负责不断检查这一点,请记住,您可能会在此方法中最终遇到竞争条件,您的用户已取消,但线程在测试之前已完成CancellationPending.

Thread.Abort() 另一方面,在强制取消该线程的线程执行中抛出一个异常,如果在执行过程中突然引发了这个异常,你必须小心可能存在危险.

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

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


ajs*_*410 5

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

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

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


Doo*_*nky 5

我想指出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();
}
Run Code Online (Sandbox Code Playgroud)

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

但是,当 IsBackground 属性设置为 false(默认情况下)并且关闭窗口时,只有窗口会消失,但进程仍会继续运行。

BackgroundWorker 类利用在后台运行的线程。