【问题标题】:Diffrernce between BackgroundWorker.ReportProgress() and Control.BeginInvoke()BackgroundWorker.ReportProgress() 和 Control.BeginInvoke() 之间的区别
【发布时间】:2010-05-19 13:39:03
【问题描述】:

下面选项1和2有什么区别?

    private void BGW_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i=1; i<=100; i++)
        {
            string txt = i.ToString();
            if (Test_Check.Checked)
                //OPTION 1
                Test_BackgroundWorker.ReportProgress(i, txt); 
            else
                //OPTION 2
                this.BeginInvoke((Action<int, string>)UpdateGUI, 
                                  new object[] {i, txt});
        }
    }

    private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        UpdateGUI(e.ProgressPercentage, (string)e.UserState);
    }

    private void UpdateGUI(int percent, string txt)
    {
        Test_ProgressBar.Value = percent;
        Test_RichTextBox.AppendText(txt + Environment.NewLine);
    }

查看反射器,Control.BeginInvoke() 似乎使用:

this.FindMarshalingControl().MarshaledInvoke(this, method, args, 1);

这似乎最终调用了一些像 PostMessage() 这样的原生函数,但无法准确计算出反射器的流程(讨厌的编译器 goto 优化)

而 BackgroundWorker.Invoke() 似乎使用:

this.asyncOperation.Post(this.progressReporter, args);

这似乎最终调用了 ThreadPool.QueueUserWorkItem()

(我只是猜测这些是每种情况的相关函数调用。)如果我理解正确,使用 ThreadPool 不能保证执行顺序,而使用 Post 机制可以。也许这将是一个潜在的差异? (编辑 - 我无法综合这种情况 - 在这两种情况下似乎都保留了调用顺序,至少在我的简单测试中是这样。)

谢谢!

【问题讨论】:

    标签: winforms multithreading controls backgroundworker invoke


    【解决方案1】:

    它们都是一样的。您在BackgroundWorker 中看到的呼叫使用SynchronizationContext。确实Post()方法的默认实现使用了线程池,但是当启动一个Windows Forms应用程序时,默认的同步上下文被WindowsFormsSynchronizationContext替换,实际上调用了Control.BeginInvoke()

    【讨论】:

      【解决方案2】:

      一个很大的区别是Control.Invoke 将阻塞直到UpdateGUI 调用被执行和完成,而BackgroundWorker.ReportProgress不会 阻塞(它会立即返回,之前 BackgroundWorker 引发事件)。

      如果您希望它们的行为相同,请改为调用 Control.BeginInvoke(不会阻止)。

      【讨论】:

      • 谢谢,但这不是我的本意——我只是修改了问题
      • 我的答案是一样的。 ReportProgress 是异步的,Control.Invoke 是同步的。在进度条的情况下,选择其中一个可能没有明显的效果。如果您使用 ReportProgress,GUI 更新将几乎是即时的 - 唯一的原因是如果您的表单正忙于同时处理一堆其他事件。
      • 是的,但我现在要问的是 ReportProgress 和 BeginInvoke 之间的区别
      • 如果你的 UpdateGUI 方法做了很多耗时的工作,差异会更加显着。那么你的选择是在以最大速度运行后台进程但延迟 GUI 更新 (ReportProgress) 或获取实时 GUI 更新但减慢后台进程 (Control.Invoke)。
      • 哦,抱歉,我没有注意到 BeginInvoke 的变化。忘记我在 cmets 中所说的话——你似乎已经意识到了那些东西。 :)
      【解决方案3】:

      我发现了一个显着的差异。在 BGW 运行时关闭表单会导致 this.Invoke() 和 this.BeginInvoke() 抛出 ObjectDisposedException。 BGW ReportProgress 机制规避了这一点。为了享受两全其美,以下模式效果很好

      public partial class MyForm : Form
      {
          private void InvokeViaBgw(Action action)
          {
              Packing_Worker.ReportProgress(0, action);
          }
      
          private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
          {
              if (this.IsDisposed) return; //You are on the UI thread now, so no race condition
      
              var action = (Action)e.UserState;
              action();
          }
      
          private private void BGW_DoWork(object sender, DoWorkEventArgs e)
          {
             //Sample usage:
             this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-25
        • 2020-05-10
        • 2014-09-20
        • 2010-10-28
        • 2015-10-04
        • 2012-08-12
        相关资源
        最近更新 更多