代码之家  ›  专栏  ›  技术社区  ›  Unplug

BackgroundWorker和Dispatcher.Invoke

  •  0
  • Unplug  · 技术社区  · 12 年前

    像许多应用程序一样,我希望在应用程序进行长时间计算时更新我的状态文本。我读过关于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窗体中。我一把LongComputational放在BackgroundWorker中,结果就不会显示。

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

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

    我希望把更新放在另一个线程中会起作用。但事实并非如此。

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

    2 回复  |  直到 12 年前
        1
  •  1
  •   Jason    12 年前

    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();
            };
    
        2
  •  0
  •   Christian Larsson    5 年前

    我解决了将Dispatcher从表单的Load主线程存储,然后从BackgroundWorker线程中存储的成员变量调用Dispatcher的问题:

    正在表单开头声明成员变量:

    Dispatcher mDispatcherMain = null;
    

    将调度程序存储在表单的加载功能中:

    mDispatcherMain = Dispatcher.CurrentDispatcher;
    

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

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