.NET BackgroundWorker:我不明白ReportProgress是如何工作的

时间:2013-10-21 16:08:20

标签: c# .net wpf

我在UI(WPF)线程中创建了BackgroundWorker(mainBw)。它有一个无限循环,它休眠1.5秒并通过Application.Current.Dispatcher.Invoke调用一个函数,它只是将“全局”text变量中的文本输出到TextBox。

同样在循环之前,它创建了另一个(child)BackgroundWorker,其中ReportsProgress在ProgressChanged事件处理程序中修改了text变量。

我认为它不起作用,因为在mainBw循环中没有像WinForms Application.DoEvents()这样的东西,所以它无法处理事件处理程序。但它的确有效。为什么呢?

以下是代码:

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace WpfApplication6
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private BackgroundWorker mainBw = new BackgroundWorker();

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            mainBw.DoWork += MainBwOnDoWork;

            mainBw.RunWorkerAsync();

            btn.IsEnabled = false;
        }

        private string text = "abc";

        private void MainBwOnDoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bw = new BackgroundWorker();

            bw.DoWork += BwOnDoWork;
            bw.ProgressChanged += BwOnProgressChanged;
            bw.WorkerReportsProgress = true;

            bw.RunWorkerAsync();

            while (true)
            {
                Thread.Sleep(1500);

                text += " main ";

                Application.Current.Dispatcher.Invoke(new Action(() => { WriteToUIThread(); }));
            }
        }

        private void WriteToUIThread()
        {
            tbox.Text = DateTime.Now + " " + text + Environment.NewLine + tbox.Text;
        }

        private void BwOnProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            text += e.UserState.ToString();
        }

        private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
        {
            while (true)
            {
                Thread.Sleep(3000);

                (sender as BackgroundWorker).ReportProgress(0, "child");
            }

        }
    }
}

    // XAML
<Window x:Class="WpfApplication6.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Name="btn" Content="Button" HorizontalAlignment="Left" Height="105" Margin="43,47,0,0" VerticalAlignment="Top" Width="165" Click="Button_Click"/>
        <TextBox Name="tbox" HorizontalAlignment="Left" Height="114" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" Margin="27,182,0,0"/>
    </Grid>
</Window>

3 个答案:

答案 0 :(得分:2)

BackgroundWorker使用通用方法获取在UI线程上运行的代码,它使用静态SynchronizationContext.Current属性来查找同步提供程序。 ReportProgress()使用其Post()方法封送调用。

如果您运行Winforms应用程序,则Current属性将引用WindowsFormsSynchronizationContext class的实例。在创建表单或调用Application.Run()时自动安装。它使用Control.Begin / Invoke()来实现Post和Send方法。

如果您运行WPF应用程序,那么Current属性将引用DispatcherSynchronizationContext的实例,它使用Dispatcher.Begin / Invoke()。

所以这只是自动生效。

答案 1 :(得分:1)

它的工作原理是因为BackgroundWorker在后​​台线程中起作用(因此名称)。因为它没有在UI线程中运行,所以它不会阻塞UI线程,它只是偶尔发送短的方法在UI线程中运行。

尽管如此,它仍然不是解决问题的特别好的方法。如果您想每3秒运行一些代码,请使用Timer代替。如果您在Forms命名空间中使用计时器,它将代表您在UI线程中触发它的事件。

答案 2 :(得分:0)

它有效,因为BackgroundWorker运行 - 正如其名称所示 - 在后台线程中独立于主UI线程运行。

ReportProgress - 事件被编组到UI线程,以便可以轻松处理此事件,而无需调用Invoke - 所涉及的控件上的方法。

Contraray,WinForms中的Application.DoEvents() - 方法允许进程处理其他消息,以便在主线程中执行长时间操作,不使用使用后台线程。