WPF Dispatcher.BeginInvoke 和 UI/后台线程

发布于 2024-07-29 03:21:22 字数 1470 浏览 6 评论 0原文

我想我需要一些关于 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 em> 到我的 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 很奇怪,所以我很想知道一些替代方案。

I think I need some clarifications regarding WPFs Dispatcher.Invoke and Dispatcher.BeginInvoke usage.

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

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 workAction is being performed. This is because Dispatcher invokes always run on the UI thread, right?

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

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 BackgroundWorker is quirky, so I am curious to know of some alternatives.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

春夜浅 2024-08-05 03:21:22

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

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

默嘫て 2024-08-05 03:21:22

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

简单,不是吗?

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

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?

じее 2024-08-05 03:21:22

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

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

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

爱本泡沫多脆弱 2024-08-05 03:21:22

正如其名称所示,它将在后台执行,因此您无需使用调度程序实例化它。 另外,如果您希望此代码运行到 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 。 否则,您将面临终止事件触发的一些不一致问题。

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.

My recommendation is to create the BackgroundWorker as:

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.

不美如何 2024-08-05 03:21:22

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

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...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文