0

像许多应用程序一样,我想在应用程序进行长时间计算时更新我的​​状态文本。我读过有关 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);

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

我可以做些什么来确保状态反映正确的程序状态?

4

2 回答 2

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();
        };
于 2013-11-03T03:24:15.297 回答
0

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

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

Dispatcher mDispatcherMain = null;

将调度程序存储在表单的 Load-function 中:

mDispatcherMain = Dispatcher.CurrentDispatcher;

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

mDispatcherMain.Invoke(new Action(() => { /* What you want to do */ }));
于 2020-10-02T14:18:06.373 回答