【问题标题】:BackgroundWorker and Dispatcher.InvokeBackgroundWorker 和 Dispatcher.Invoke
【发布时间】:2013-11-03 02:44:57
【问题描述】:

像许多应用程序一样,我想在应用程序进行长时间计算时更新我的​​状态文本。我读过关于 Dispatcher 和 BackgroundWorker 的文章。我知道我肯定需要确保更新发生在 UI 线程中。我的第一次尝试是:

MyView.UpdateStatus( "Please wait" );
LongComputation();
MyView.UpdateStatus( "Ready" );

这不起作用,因为(我认为)LongComputation 阻止了状态被更新。

所以我尝试这样做:

BackgroundWorker worker = new BackgroundWorker();
MyView.UpdateStatus( "Please wait");
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    LongComputation();
}
worker.RunWorkerAsync();
MyView.UpdateStatus( "Ready" );

我希望额外的线程会给 UpdateStatus 一个更新状态文本的机会。它也不起作用。原因之一是 LongComputation 的结果显示在 Windows 窗体中。一旦我将 LongComputation 放入 BackgroundWorker 中,结果就不会显示出来。

所以我第三次尝试使用流动代码:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    Dispatcher.Invoke(((Action)(() => Status.Text = args.Argument as string)));
};

worker.RunWorkerAsync(newStatus);

我希望将更新放在另一个线程中会起作用。但它没有。

如何确保状态反映正确的程序状态?

【问题讨论】:

  • LongComputation 是否返回 UI 正在等待的内容?
  • 不,它在一些复杂的计算之后绘制了一个折线图。

标签: wpf backgroundworker dispatch


【解决方案1】:

BackgroundWorker 使用 RunWorkerCompleted 和 ReportProgress 事件与主线程进行通信。 RunWorkerCompleted 应该做您需要做的事情,因为它会在后台工作完成后立即在 UI 线程上执行。

        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            LongComputation();
        };

        // RunWorkerCompleted will fire on the UI thread when the background process is complete
        worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
        {
            if (args.Error != null)
            {
                // an exception occurred on the background process, do some error handling
            }

            MyView.UpdateStatus("Ready");
        };

        MyView.UpdateStatus("Please wait");
        worker.RunWorkerAsync();

此外,您可以使用 RunWorkerCompleted 通过 DoWorkerEventArgs 的 Result 属性将结果编组回主线程。

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            args.Result = LongComputation();
        };

        worker.rep

        // RunWorkerCompleted will fire on the UI thread when the background process is complete
        worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
        {
            if (args.Error != null)
            {
                // an exception occurred on the background process, do some error handling
            }

            var result = args.Result;

            // do something on the UI with your result

            MyView.UpdateStatus("Ready");
        };

最后,您可以在后台进程的逻辑步骤中使用 ReportProgress 事件更新您的 UI:

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            FirstHalfComputation();

            // you can report a percentage back to the UI thread, or you can send 
            // an object payload back
            int completedPercentage = 50;
            object state = new SomeObject();
            worker.ReportProgress(completedPercentage , state); 

            SecondHalfComputation();
        };

        worker.WorkerReportsProgress = true;    // this is important, defaults to false
        worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
        {
            int completedPercentage = args.ProgressPercentage;
            SomeObject state = args.UserState as SomeObject

            // update a progress bar or do whatever makes sense
            progressBar1.Step = completedPercentage;
            progressBar1.PerformStep();
        };

【讨论】:

  • 我可以在非 UI 线程中做MyWindowsForm newForm = new MyWindowsForm(args); newForm.Show() 吗?
  • 我尝试了你的建议,newForm 没有像我预期的那样弹出。当这两条线不在 BackgroundWorker 线程中时,它会与折线图一起显示。
  • 弹出新表单应该在 RunWorkerCompleted 中。它在 UI 线程上执行。如果您需要 LongComputation 中的 args(对于 MyWindowsForm(args)),则使用 DoWorkerEventArgs Result 属性将其传递回 RunWorkerCompleted。
  • 谢谢杰森。我应用了你所说的并使我的应用程序工作。我有一个后续问题。如果我在没有源代码的引用中调用一个函数并且该函数需要很长时间才能生成图形怎么办?我不能使用 BackgroundWorker,因为该函数需要在 UI 线程中执行。
  • 没关系。我使用 Dispatcher.Invoke 更新状态并将优先级设置为Send,这是最高优先级。虽然用户还需要等待,但至少她知道发生了什么。
【解决方案2】:

我解决了我的问题,即在表单加载时从主线程存储调度程序,然后从我从 BackgroundWorker 线程存储的成员变量调用调度程序:

在表格开头声明成员变量:

Dispatcher mDispatcherMain = null;

将调度程序存储在表单的加载函数中:

mDispatcherMain = Dispatcher.CurrentDispatcher;

从 BackgroundWorker 的 DoWork 函数调用主线程:

mDispatcherMain.Invoke(new Action(() => { /* What you want to do */ }));

【讨论】:

    猜你喜欢
    • 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
    相关资源
    最近更新 更多