0

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

同样在循环之前,它创建了另一个(child)BackgroundWorker,它在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>
4

3 回答 3

2

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

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

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

所以这只是自动工作。

于 2013-10-21T16:13:59.607 回答
1

它之所以有效,是因为它BackgroundWorker确实在后台线程中工作(因此得名)。由于它没有在 UI 线程中运行,因此它不会阻塞 UI 线程,它只是每隔一段时间发送一些短方法在 UI 线程中运行。

也就是说,它仍然不是解决问题的特别精心设计的方法。如果您想每 3 秒运行一次代码,请使用 aTimer代替。如果您在Forms命名空间中使用计时器,它将代表您在 UI 线程中触发它的事件。

于 2013-10-21T16:11:16.253 回答
0

它可以工作,因为BackgroundWorker运行 - 正如它的名字所说 - 在独立于主 UI 线程的后台线程中运行。

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

相反,Application.DoEvents()WinForms 中的 -method 允许进程在使用后台线程的情况下处理其他消息以在主线程中执行长时间操作。

于 2013-10-21T16:11:34.600 回答