【问题标题】:WPF Dispatcher.BeginInvoke and UI/Background ThreadsWPF Dispatcher.BeginInvoke 和 UI/后台线程
【发布时间】:2016-08-11 03:04:28
【问题描述】:

我想我需要对 WPF Dispatcher.InvokeDispatcher.BeginInvoke 的用法进行一些说明。

假设我有一些长时间运行的“工作”代码,例如在一个简单的 WPF 应用程序中按下按钮即可调用:

longWorkTextBox.Text = "Ready For Work!";
Action workAction = delegate
    {
    Console.WriteLine("Starting Work Action");
    int i = int.MaxValue;
    while (i > 0)
        i--;
    Console.WriteLine("Ending Work Action");
    longWorkTextBox.Text = "Work Complete";
    };
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);

此代码在执行 workAction 时锁定了我的用户界面。这是因为 Dispatcher 调用总是在 UI 线程上运行,对吧?

假设这一点,将调度程序配置为在与我的 UI 不同的线程中执行 workAction 的最佳做法是什么?我知道我可以添加一个 BackgroundWorker 到我的 workAction 以防止我的 UI 被锁定:

longWorkTextBox.Text = "Ready For Work!";
Action workAction = delegate
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += delegate
    {
        Console.WriteLine("Starting Slow Work");
        int i = int.MaxValue;
        while (i > 0)
        i--;
        Console.WriteLine("Ending Work Action");
    };
    worker.RunWorkerCompleted += delegate
    {
        longWorkTextBox.Text = "Work Complete";
    };
    worker.RunWorkerAsync();
 };
 longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);

除了使用BackgroundWorker,还有其他更优雅的方法吗?我一直听说 BackgroundWorker 很古怪,所以我很想知道一些替代方案。

【问题讨论】:

    标签: wpf multithreading user-interface dispatcher


    【解决方案1】:

    老实说,我认为BackgroundWorker 是最优雅的解决方案。我想不出更简单的方法。

    【讨论】:

    • 是的。您想在后台线程中做某事,但完成后会影响 UI 线程,BackgroundWorker 几乎是最容易使用的东西。
    • 2013年有一种更简单的方式——async/await:stackoverflow.com/questions/12654968/…
    • 我真的很喜欢异步,但它不能很好地处理所有事情。但这是一个相当不错的选择!
    【解决方案2】:

    我也不喜欢BackgroundWorker。 一个简单的替代方案可以是:

    using System;
    using System.Threading;
    using System.Windows;
    
    namespace Sample
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            protected override void OnSourceInitialized(EventArgs e)
            {
                 base.OnSourceInitialized(e);
                 longWorkTextBox.Text = "Ready For Work!";
           }
    
            private void startButton_Click(object sender, RoutedEventArgs e)
            {
                new Thread(Work).Start();
            }
    
            void Work()
            {
                longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; }));
                Console.WriteLine("Starting Work Action");
                int i = int.MaxValue;
                while (i > 0)
                    i--;
                Console.WriteLine("Ending Work Action");
                longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; }));
            }
        }
    }
    

    简单,不是吗?

    【讨论】:

      【解决方案3】:

      查理的答案是你正在寻找的,真的。

      但是,如果可能的话,您可能会考虑是否可以打包您的工作,以使各个工作单元很小并且不会对 UI 产生太大影响。这将允许您直接使用 Dispatcher。 WPF 线程页面上有一个很好的例子:https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx

      【讨论】:

      • 关于这个主题的精彩文章!
      • 是的...通常 MSDN 是最低限度的,但那篇文章真的很透彻。
      • 有关 WPF 中异步编程的更多信息,您可以阅读:a2zdotnet.com/View.aspx?Id=93
      【解决方案4】:

      正如其名称所示,它将在后台执行,因此您无需使用 Dispatcher 实例化它。另外,如果您希望此代码运行到 WP7 中,则 BeginInvoke 不会获取背景参数。

      我的建议是将 BackgroundWorker 创建为:

      BackgroundWorker worker = new BackgroundWorker;
      

      然后为事件创建处理程序:

      worker.WorkerReportsProgress = true;
      worker.WorkerSupportsCancellation = true;
      worker.DoWork +=new DoWorkEventHandler(worker_DoWork);
      worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
      worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged);
      

      最后你打电话:

      bkwkPlayingLoop.RunWorkerAsync();
      

      从 DoWork 内部使用 Dispatcher 而是调用 worker.ReportProgress() 并从那里处理 UI 是一个很大的诱惑。否则,您将面临一些与终止事件的触发不一致的情况。

      【讨论】:

        【解决方案5】:

        任务比后台工作人员更容易使用,做的事情更多,问题更少,而且几乎是创建的,所以后台工作人员不再需要使用......

        【讨论】:

        • 你可以举一些例子吗?
        • 后台工作人员已被任务有效地弃用...blog.stephencleary.com/2013/05/…
        • 嗯..感谢这个链接。我正在评估一种在 WPF 中执行后台任务的好方法。我想我会深入了解这个系列。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多