WPF Dispatcher.BeginInvoke 和 UI/后台线程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1207832/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 20:47:23  来源:igfitidea点击:

WPF Dispatcher.BeginInvoke and UI/Background Threads

wpfmultithreadinguser-interfacedispatcher

提问by Matthew Ruston

I think I need some clarifications regarding WPFs Dispatcher.Invokeand Dispatcher.BeginInvokeusage.

我想我需要一些关于 WPFs Dispatcher.InvokeDispatcher.BeginInvoke用法的说明。

Suppose I have some long running 'work' code like such that is invoked on the press of a button in a simple WPF application:

假设我有一些长时间运行的“工作”代码,例如在简单的 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);

This code is locking up my user interface while the workActionis being performed. This is because Dispatcher invokes always run on the UI thread, right?

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

Assuming this, what is the best practice for configuring my dispatcher to execute the workActionin a separate thread from my UI?I know I can add a BackgroundWorkerto my workActionto prevent my UI from locking as such:

假设这一点,配置我的调度程序以在与我的 UI 不同的线程中执行workAction的最佳实践是什么?我知道我可以在我的workAction 中添加一个BackgroundWorker来防止我的 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);

Is there any more elegant ways of doing this besides using the BackgroundWorker? I've always heard that the BackgroundWorkeris quirky, so I am curious to know of some alternatives.

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

回答by Charlie

I honestly think the BackgroundWorkeris the most elegant solution for this. I cannot think of a simpler way to do it.

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

回答by Anderson Imes

Charlie's answer is what you are looking for, really.

查理的答案正是您要找的,真的。

However, if it's possible you might look at whether or not you can parcel up your work so that the individual units of work are small and don't affect the UI as much. This would allow you to just use the Dispatcher directly. There is a good example of this on the WPF Threading page: https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx

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

回答by italianogrosso

Me too don't like BackgroundWorker. A simple alternative can be something like:

我也不喜欢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"; }));
        }
    }
}

Easy, not?

容易,不是吗?

回答by Julio Bailon

As its name indicates it will execute in the Background so you don't need to instantiate it with the Dispatcher. Plus if you want this code to run into a WP7 the BeginInvoke does not get the background parameter.

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

My recommendation is to create the BackgroundWorker as:

我的建议是将 BackgroundWorker 创建为:

BackgroundWorker worker = new BackgroundWorker;

And then create the handlers for the events:

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

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

And finally you call:

最后你打电话:

bkwkPlayingLoop.RunWorkerAsync();

It is a big temptation to use the Dispatcher from inside the DoWork but instead call worker.ReportProgress() and handle the UI from there. You will otherwise face some inconsistencies with the firing of termination events.

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

回答by MattE

Tasks are easier to use than Background workers, do more things, have fewer issues and were pretty much created so Background Workers didn't need to be used anymore...

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