18

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

4

5 回答 5

25

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

于 2009-07-30T17:05:11.440 回答
4

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

容易,不是吗?

于 2012-05-30T05:52:26.030 回答
4

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

于 2009-07-30T22:01:00.390 回答
2

正如其名称所示,它将在后台执行,因此您无需使用 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 是一个很大的诱惑。否则,您将面临一些与终止事件的触发不一致的情况。

于 2011-08-22T20:26:47.850 回答
1

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

于 2016-08-10T19:04:28.710 回答